Version 2.9.0-1.0.dev
Merge commit 'ae0eca83188ce3bd99e3f012cf7eec4b0bdf2c4f' into dev
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 4cbffad..60c449a 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -44,7 +44,7 @@
runtime_lib = 'lib/_internal/js_dev_runtime/private/ddc_runtime/'
for nnbd_file in files:
if nnbd_file.startswith('sdk_nnbd/' + runtime_lib):
- file = 'sdk/' + runtime_lib + nnbd_file[4:]
+ file = 'sdk/' + nnbd_file[9:]
if not file in files:
unsynchronized_files.append(file)
if unsynchronized_files:
diff --git a/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart b/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
index 23ca3f4..80f3f5a 100644
--- a/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/dartfix_info.dart
@@ -63,6 +63,9 @@
LintFixInfo.preferAdjacentStringConcatenation,
LintFixInfo.preferCollectionLiterals,
LintFixInfo.preferConditionalAssignment,
+ LintFixInfo.preferConstConstructors,
+ LintFixInfo.preferConstConstructorsInImmutables,
+ LintFixInfo.preferConstDeclarations,
LintFixInfo.preferContains,
LintFixInfo.preferEqualForDefaultValues,
LintFixInfo.preferFinalFields,
@@ -322,6 +325,24 @@
isPedantic: true,
);
+ static final preferConstConstructors = LintFixInfo(
+ 'prefer_const_constructors',
+ DartFixKind.ADD_CONST,
+ 'Make the instantiation const.',
+ );
+
+ static final preferConstConstructorsInImmutables = LintFixInfo(
+ 'prefer_const_constructors_in_immutables',
+ DartFixKind.ADD_CONST,
+ 'Make the constructor const.',
+ );
+
+ static final preferConstDeclarations = LintFixInfo(
+ 'prefer_const_declarations',
+ DartFixKind.REPLACE_FINAL_WITH_CONST,
+ 'Make the declaration const.',
+ );
+
static final preferContains = LintFixInfo(
'prefer_contains',
DartFixKind.CONVERT_TO_CONTAINS,
@@ -423,7 +444,7 @@
static final preferSingleQuotes = LintFixInfo(
'prefer_single_quotes',
DartFixKind.CONVERT_TO_SINGLE_QUOTED_STRING,
- 'Convert strings using a dobule quote to use a single quote.',
+ 'Convert strings using a double quote to use a single quote.',
isPedantic: true,
);
diff --git a/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart b/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
index 35c6e54..3a695592 100644
--- a/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
+++ b/pkg/analysis_server/lib/src/edit/fix/non_nullable_fix.dart
@@ -12,11 +12,11 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/task/options.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:charcode/charcode.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
+import 'package:pub_semver/pub_semver.dart';
import 'package:source_span/source_span.dart';
import 'package:yaml/yaml.dart';
@@ -28,6 +28,13 @@
/// mature enough.
static const bool _usePermissiveMode = true;
+ // TODO(srawlins): Refactor to use
+ // `Feature.non_nullable.firstSupportedVersion` when this becomes non-null.
+ static const String _intendedMinimumSdkVersion = '2.8.0';
+
+ static const String _intendedSdkVersionConstraint =
+ '>=$_intendedMinimumSdkVersion <3.0.0';
+
final int preferredPort;
final DartFixListener listener;
@@ -96,101 +103,102 @@
}
}
- /// If the package contains an analysis_options.yaml file, then update the
- /// file to enabled NNBD. If that file does not exist, but the package
- /// contains a pubspec.yaml, then create the analysis_options.yaml file.
+ /// Update the pubspec.yaml file to specify a minimum Dart SDK version which
+ /// enables the Null Safety feature.
@override
Future<void> processPackage(Folder pkgFolder) async {
if (!_packageIsNNBD) {
return;
}
- // TODO(danrubel): Update pubspec.yaml to enable NNBD
-
- var optionsFile = pkgFolder.getChildAssumingFile('analysis_options.yaml');
- String optionsContent;
- YamlNode optionsMap;
- if (optionsFile.exists) {
- try {
- optionsContent = optionsFile.readAsStringSync();
- } on FileSystemException catch (e) {
- processYamlException('read', optionsFile.path, e);
- return;
- }
- try {
- optionsMap = loadYaml(optionsContent);
- } on YamlException catch (e) {
- processYamlException('parse', optionsFile.path, e);
- return;
- }
+ var pubspecFile = pkgFolder.getChildAssumingFile('pubspec.yaml');
+ String pubspecContent;
+ YamlNode pubspecMap;
+ if (!pubspecFile.exists) {
+ // TODO(srawlins): Handle other package types, such as Bazel.
+ return;
}
- SourceSpan parentSpan;
- String content;
- YamlNode analyzerOptions;
- if (optionsMap is YamlMap) {
- analyzerOptions = optionsMap.nodes[AnalyzerOptions.analyzer];
+ try {
+ pubspecContent = pubspecFile.readAsStringSync();
+ } on FileSystemException catch (e) {
+ processYamlException('read', pubspecFile.path, e);
+ return;
}
- if (analyzerOptions == null) {
- var start = SourceLocation(0, line: 0, column: 0);
- parentSpan = SourceSpan(start, start, '');
- content = '''
-analyzer:
- enable-experiment:
- - non-nullable
-
-''';
- } else if (analyzerOptions is YamlMap) {
- var experiments = analyzerOptions.nodes[AnalyzerOptions.enableExperiment];
- if (experiments == null) {
- parentSpan = analyzerOptions.span;
- content = '''
-
- enable-experiment:
- - non-nullable''';
- } else if (experiments is YamlList) {
- experiments.nodes.firstWhere(
- (node) => node.span.text == EnableString.non_nullable,
- orElse: () {
- parentSpan = experiments.span;
- content = '''
-
- - non-nullable''';
- return null;
- },
- );
- }
+ try {
+ pubspecMap = loadYaml(pubspecContent);
+ } on YamlException catch (e) {
+ processYamlException('parse', pubspecFile.path, e);
+ return;
}
- if (parentSpan != null) {
- final space = ' '.codeUnitAt(0);
- final cr = '\r'.codeUnitAt(0);
- final lf = '\n'.codeUnitAt(0);
-
+ /// Inserts [content] into [pubspecFile], immediately after [parentSpan].
+ void insertAfterParent(SourceSpan parentSpan, String content) {
var line = parentSpan.end.line;
var offset = parentSpan.end.offset;
+ // Walk [offset] and [line] back to the first non-whitespace character
+ // before [offset].
while (offset > 0) {
- var ch = optionsContent.codeUnitAt(offset - 1);
- if (ch == space || ch == cr) {
+ var ch = pubspecContent.codeUnitAt(offset - 1);
+ if (ch == $space || ch == $cr) {
--offset;
- } else if (ch == lf) {
+ } else if (ch == $lf) {
--offset;
--line;
} else {
break;
}
}
+ var edit = SourceEdit(offset, 0, content);
listener.addSourceFileEdit(
- 'enable non-nullable analysis',
- Location(
- optionsFile.path,
- offset,
- content.length,
- line,
- 0,
- ),
- SourceFileEdit(optionsFile.path, 0,
- edits: [SourceEdit(offset, 0, content)]));
+ 'enable Null Safety language feature',
+ Location(pubspecFile.path, offset, content.length, line, 0),
+ SourceFileEdit(pubspecFile.path, 0, edits: [edit]));
+ }
+
+ void replaceSpan(SourceSpan span, String content) {
+ var line = span.start.line;
+ var offset = span.start.offset;
+ var edit = SourceEdit(offset, span.length, content);
+ listener.addSourceFileEdit(
+ 'enable Null Safety language feature',
+ Location(pubspecFile.path, offset, content.length, line, 0),
+ SourceFileEdit(pubspecFile.path, 0, edits: [edit]));
+ }
+
+ YamlNode environmentOptions;
+ if (pubspecMap is YamlMap) {
+ environmentOptions = pubspecMap.nodes['environment'];
+ }
+ if (environmentOptions == null) {
+ var start = SourceLocation(0, line: 0, column: 0);
+ var content = '''
+environment:
+ sdk: '$_intendedSdkVersionConstraint'
+
+''';
+ insertAfterParent(SourceSpan(start, start, ''), content);
+ } else if (environmentOptions is YamlMap) {
+ var sdk = environmentOptions.nodes['sdk'];
+ if (sdk == null) {
+ var content = """
+
+ sdk: '$_intendedSdkVersionConstraint'""";
+ insertAfterParent(environmentOptions.span, content);
+ } else if (sdk is YamlScalar) {
+ var currentConstraint = VersionConstraint.parse(sdk.value);
+ var minimumVersion = Version.parse('2.8.0');
+ if (currentConstraint is VersionRange &&
+ currentConstraint.min >= minimumVersion) {
+ // The current SDK version constraint already enables Null Safety.
+ return;
+ } else {
+ // TODO(srawlins): This overwrites the current maximum version. In
+ // the uncommon situation that the maximum is not '<3.0.0', it should
+ // not.
+ replaceSpan(sdk.span, "'$_intendedSdkVersionConstraint'");
+ }
+ }
}
}
@@ -219,11 +227,11 @@
$optionsFilePath
$error
- Manually update this file to enable non-nullable by adding:
+ Manually update this file to enable the Null Safety language feature by
+ adding:
- analyzer:
- enable-experiment:
- - non-nullable
+ environment:
+ sdk: '$_intendedSdkVersionConstraint';
''');
_packageIsNNBD = false;
}
diff --git a/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart b/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart
index 4c1bbe1..692059e 100644
--- a/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart
+++ b/pkg/analysis_server/lib/src/edit/nnbd_migration/info_builder.dart
@@ -125,10 +125,26 @@
}
/// Return an edit that can be applied.
- List<EditDetail> _computeEdits(AtomicEditInfo fixInfo, int offset) {
+ List<EditDetail> _computeEdits(
+ AtomicEditInfo fixInfo, int offset, String content) {
+ EditDetail _removeHint(String description) => EditDetail.fromSourceEdit(
+ description,
+ fixInfo.hintComment.changesToRemove(content).toSourceEdits().single);
+
+ EditDetail _changeHint(String description, String replacement) =>
+ EditDetail.fromSourceEdit(
+ description,
+ fixInfo.hintComment
+ .changesToReplace(content, replacement)
+ .toSourceEdits()
+ .single);
+
var edits = <EditDetail>[];
var fixKind = fixInfo.description.kind;
switch (fixKind) {
+ case NullabilityFixKind.addLateDueToHint:
+ edits.add(_removeHint('Remove /*late*/ hint'));
+ break;
case NullabilityFixKind.addRequired:
// TODO(brianwilkerson) This doesn't verify that the meta package has
// been imported.
@@ -141,7 +157,7 @@
edits.add(EditDetail('Add /*!*/ hint', offset, 0, '/*!*/'));
break;
case NullabilityFixKind.checkExpressionDueToHint:
- edits.add(EditDetail('Remove /*!*/ hint', offset, 5, ''));
+ edits.add(_removeHint('Remove /*!*/ hint'));
break;
case NullabilityFixKind.downcastExpression:
case NullabilityFixKind.otherCastExpression:
@@ -162,12 +178,12 @@
edits.add(EditDetail('Add /*?*/ hint', offset, 0, '/*?*/'));
break;
case NullabilityFixKind.makeTypeNullableDueToHint:
- edits.add(EditDetail('Add /*!*/ hint', offset, 5, '/*!*/'));
- edits.add(EditDetail('Remove /*?*/ hint', offset, 5, ''));
+ edits.add(_changeHint('Change to /*!*/ hint', '/*!*/'));
+ edits.add(_removeHint('Remove /*?*/ hint'));
break;
case NullabilityFixKind.typeNotMadeNullableDueToHint:
- edits.add(EditDetail('Remove /*!*/ hint', offset, 5, ''));
- edits.add(EditDetail('Add /*?*/ hint', offset, 5, '/*?*/'));
+ edits.add(_removeHint('Remove /*!*/ hint'));
+ edits.add(_changeHint('Change to /*?*/ hint', '/*?*/'));
break;
}
return edits;
@@ -275,6 +291,7 @@
var regions = unitInfo.regions;
var lineInfo = result.unit.lineInfo;
var insertions = <int, List<AtomicEdit>>{};
+ var hintsSeen = <HintComment>{};
// Apply edits and build the regions.
var changes = sourceInfo.changes ?? {};
@@ -296,31 +313,35 @@
(insertions[sourceOffset] ??= []).add(AtomicEdit.insert(replacement));
}
var info = edit.info;
- var edits = info != null ? _computeEdits(info, sourceOffset) : [];
+ var edits = info != null
+ ? _computeEdits(info, sourceOffset, result.content)
+ : [];
var lineNumber = lineInfo.getLocation(sourceOffset).lineNumber;
var traces = info == null ? const [] : _computeTraces(info.fixReasons);
var description = info?.description;
+ var hint = info?.hintComment;
+ var isCounted = hint == null || hintsSeen.add(hint);
if (description != null) {
var explanation = description.appliedMessage;
var kind = description.kind;
if (edit.isInformative) {
regions.add(RegionInfo(RegionType.informative, offset,
- replacement.length, lineNumber, explanation, kind,
+ replacement.length, lineNumber, explanation, kind, isCounted,
edits: edits, traces: traces));
} else if (edit.isInsertion) {
regions.add(RegionInfo(RegionType.add, offset, replacement.length,
- lineNumber, explanation, kind,
+ lineNumber, explanation, kind, isCounted,
edits: edits, traces: traces));
} else if (edit.isDeletion) {
regions.add(RegionInfo(RegionType.remove, offset, length,
- lineNumber, explanation, kind,
+ lineNumber, explanation, kind, isCounted,
edits: edits, traces: traces));
} else if (edit.isReplacement) {
regions.add(RegionInfo(RegionType.remove, offset, length,
- lineNumber, explanation, kind,
+ lineNumber, explanation, kind, isCounted,
edits: edits, traces: traces));
regions.add(RegionInfo(RegionType.add, end, replacement.length,
- lineNumber, explanation, kind,
+ lineNumber, explanation, kind, isCounted,
edits: edits, traces: traces));
} else {
throw StateError(
diff --git a/pkg/analysis_server/lib/src/edit/nnbd_migration/migration_info.dart b/pkg/analysis_server/lib/src/edit/nnbd_migration/migration_info.dart
index bde42b0..e563269 100644
--- a/pkg/analysis_server/lib/src/edit/nnbd_migration/migration_info.dart
+++ b/pkg/analysis_server/lib/src/edit/nnbd_migration/migration_info.dart
@@ -6,6 +6,7 @@
import 'package:analysis_server/src/edit/nnbd_migration/unit_link.dart';
import 'package:analysis_server/src/edit/preview/preview_site.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:meta/meta.dart';
@@ -29,6 +30,12 @@
/// Initialize a newly created detail.
EditDetail(this.description, this.offset, this.length, this.replacement);
+
+ /// Initializes a detail based on a [SourceEdit] object.
+ factory EditDetail.fromSourceEdit(
+ String description, SourceEdit sourceEdit) =>
+ EditDetail(description, sourceEdit.offset, sourceEdit.length,
+ sourceEdit.replacement);
}
/// A class storing rendering information for an entire migration report.
@@ -150,6 +157,9 @@
/// The kind of fix that was applied.
final NullabilityFixKind kind;
+ /// Indicates whether this region should be counted in the edit summary.
+ final bool isCounted;
+
/// A list of the edits that are related to this range.
List<EditDetail> edits;
@@ -159,7 +169,7 @@
/// Initialize a newly created region.
RegionInfo(this.regionType, this.offset, this.length, this.lineNumber,
- this.explanation, this.kind,
+ this.explanation, this.kind, this.isCounted,
{this.edits = const [], this.traces = const []});
}
@@ -277,9 +287,16 @@
return region;
}
// TODO: adjust traces
- return RegionInfo(region.regionType, region.offset + length,
- region.length, region.lineNumber, region.explanation, region.kind,
- edits: region.edits, traces: region.traces);
+ return RegionInfo(
+ region.regionType,
+ region.offset + length,
+ region.length,
+ region.lineNumber,
+ region.explanation,
+ region.kind,
+ region.isCounted,
+ edits: region.edits,
+ traces: region.traces);
}));
diskChangesOffsetMapper = OffsetMapper.sequence(
diff --git a/pkg/analysis_server/lib/src/edit/nnbd_migration/unit_renderer.dart b/pkg/analysis_server/lib/src/edit/nnbd_migration/unit_renderer.dart
index aabfdd8..4ec259f 100644
--- a/pkg/analysis_server/lib/src/edit/nnbd_migration/unit_renderer.dart
+++ b/pkg/analysis_server/lib/src/edit/nnbd_migration/unit_renderer.dart
@@ -32,6 +32,7 @@
NullabilityFixKind.addType,
NullabilityFixKind.replaceVar,
NullabilityFixKind.removeAs,
+ NullabilityFixKind.addLateDueToHint,
NullabilityFixKind.checkExpressionDueToHint,
NullabilityFixKind.makeTypeNullableDueToHint,
NullabilityFixKind.removeLanguageVersionComment
@@ -68,7 +69,7 @@
var editListsByKind = <NullabilityFixKind, List<EditListItem>>{};
for (var region in unitInfo.fixRegions) {
var kind = region.kind;
- if (kind != null) {
+ if (kind != null && region.isCounted) {
(editListsByKind[kind] ??= []).add(EditListItem(
line: region.lineNumber,
explanation: region.explanation,
@@ -240,6 +241,8 @@
String _headerForKind(NullabilityFixKind kind, int count) {
var s = count == 1 ? '' : 's';
switch (kind) {
+ case NullabilityFixKind.addLateDueToHint:
+ return '$count late hint$s converted to late keyword$s';
case NullabilityFixKind.addRequired:
return '$count required keyword$s added';
case NullabilityFixKind.downcastExpression:
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
index 10c24e1..7fd4bc0 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -4,6 +4,11 @@
/// Utility methods to compute the value of the features used for code
/// completion.
+import 'dart:math' as math;
+
+import 'package:analysis_server/src/protocol_server.dart'
+ show convertElementToElementKind;
+import 'package:analysis_server/src/services/completion/dart/relevance_tables.g.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart'
@@ -89,6 +94,29 @@
}
}
+ /// Return the value of the _element kind_ feature for the [element] when
+ /// completing at the given [completionLocation]. If a [distance] is given it
+ /// will be used to provide finer-grained relevance scores.
+ double elementKindFeature(Element element, String completionLocation,
+ {int distance}) {
+ if (completionLocation == null) {
+ return -1.0;
+ }
+ var locationTable = elementKindRelevance[completionLocation];
+ if (locationTable == null) {
+ return -1.0;
+ }
+ var kind = convertElementToElementKind(element);
+ var range = locationTable[kind];
+ if (range == null) {
+ return 0.0;
+ }
+ if (distance == null) {
+ return range.upper;
+ }
+ return range.conditionalProbability(_distanceToPercent(distance));
+ }
+
/// Return the value of the _has deprecated_ feature for the given [element].
double hasDeprecatedFeature(Element element) {
return element.hasDeprecated ? 0.0 : 1.0;
@@ -115,7 +143,7 @@
if (distance < 0) {
return 0.0;
}
- return 1.0 / (distance + 1);
+ return _distanceToPercent(distance);
}
/// Return the value of the _starts with dollar_ feature.
@@ -129,6 +157,9 @@
? -1.0
: (proposedMemberName == containingMethodName ? 1.0 : 0.0);
+ /// Convert a [distance] to a percentage value and return the percentage.
+ double _distanceToPercent(int distance) => math.pow(0.95, distance);
+
/// Return the inheritance distance between the [subclass] and the
/// [superclass]. The set of [visited] elements is used to guard against
/// cycles in the type graph.
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/probability_range.dart b/pkg/analysis_server/lib/src/services/completion/dart/probability_range.dart
index 0eefaa1..2b4be44 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/probability_range.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/probability_range.dart
@@ -18,6 +18,6 @@
/// on the event represented by this range, return the probability of the
/// event independent of the event based on this range.
double conditionalProbability(double probability) {
- return lower + (upper - lower) * probability;
+ return lower + ((upper - lower) * probability);
}
}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart
index 9b0d656..a82924b 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart
@@ -112,9 +112,13 @@
if (useNewRelevance) {
var contextType = request.featureComputer
.contextTypeFeature(request.contextType, elementType);
+ var elementKind = request.featureComputer
+ .elementKindFeature(element, request.opType.completionLocation);
var hasDeprecated = request.featureComputer.hasDeprecatedFeature(element);
relevance = _computeRelevance(
- contextType: contextType, hasDeprecated: hasDeprecated);
+ contextType: contextType,
+ elementKind: elementKind,
+ hasDeprecated: hasDeprecated);
}
var suggestion = createSuggestion(request, element,
completion: completion,
@@ -128,16 +132,15 @@
/// Compute a relevance value from the given feature scores:
/// - [contextType] is higher if the type of the element matches the context
/// type,
- /// - [hasDeprecated] is higher if the element is not deprecated,
- /// - [inheritanceDistance] is higher if the element is defined closer to the
- /// target type,
- /// - [startsWithDollar] is higher if the element's name doe _not_ start with
- /// a dollar sign, and
- /// - [superMatches] is higher if the element is being invoked through `super`
- /// and the element's name matches the name of the enclosing method.
+ /// - [elementKind] is higher if the kind of element occurs more frequently in
+ /// the given location, and
+ /// - [hasDeprecated] is higher if the element is not deprecated.
int _computeRelevance(
- {@required double contextType, @required double hasDeprecated}) {
- var score = weightedAverage([contextType, hasDeprecated], [1.0, 0.5]);
+ {@required double contextType,
+ @required double elementKind,
+ @required double hasDeprecated}) {
+ var score = weightedAverage(
+ [contextType, elementKind, hasDeprecated], [1.0, 0.75, 0.5]);
return toRelevance(score, Relevance.member);
}
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart
index 54a3100..50e59d7 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -28,15 +28,15 @@
/// An enumeration of possible assist kinds.
class DartAssistKind {
static const ADD_DIAGNOSTIC_PROPERTY_REFERENCE = AssistKind(
- 'ADD_DIAGNOSTIC_PROPERTY_REFERENCE',
+ 'dart.assist.add.diagnosticPropertyReference',
30,
'Add a debug reference to this property');
static const ADD_NOT_NULL_ASSERT = AssistKind(
- 'dart.assist.addNotNullAssert', 30, 'Add a not-null assertion');
+ 'dart.assist.add.notNullAssert', 30, 'Add a not-null assertion');
static const ADD_RETURN_TYPE =
- AssistKind('dart.assist.addReturnType', 30, 'Add return type');
+ AssistKind('dart.assist.add.returnType', 30, 'Add return type');
static const ADD_TYPE_ANNOTATION =
- AssistKind('dart.assist.addTypeAnnotation', 30, 'Add type annotation');
+ AssistKind('dart.assist.add.typeAnnotation', 30, 'Add type annotation');
static const ASSIGN_TO_LOCAL_VARIABLE = AssistKind(
'dart.assist.assignToVariable', 30, 'Assign value to new local variable');
static const CONVERT_CLASS_TO_MIXIN = AssistKind(
@@ -82,9 +82,9 @@
30,
'Convert to field formal parameter');
static const CONVERT_TO_FOR_ELEMENT = AssistKind(
- 'dart.assist.convertToForElement', 30, "Convert to a 'for' element");
+ 'dart.assist.convert.toForElement', 30, "Convert to a 'for' element");
static const CONVERT_TO_IF_ELEMENT = AssistKind(
- 'dart.assist.convertToIfElement', 30, "Convert to an 'if' element");
+ 'dart.assist.convert.toIfElement', 30, "Convert to an 'if' element");
static const CONVERT_TO_INT_LITERAL = AssistKind(
'dart.assist.convert.toIntLiteral', 30, 'Convert to an int literal');
static const CONVERT_TO_LIST_LITERAL = AssistKind(
@@ -115,7 +115,7 @@
30,
'Convert to single quoted string');
static const CONVERT_TO_SPREAD =
- AssistKind('dart.assist.convertToSpread', 30, 'Convert to a spread');
+ AssistKind('dart.assist.convert.toSpread', 30, 'Convert to a spread');
static const ENCAPSULATE_FIELD =
AssistKind('dart.assist.encapsulateField', 30, 'Encapsulate field');
static const EXCHANGE_OPERANDS =
@@ -163,7 +163,7 @@
AssistKind('dart.assist.flutter.removeWidget', 35, 'Remove this widget');
static const IMPORT_ADD_SHOW = AssistKind(
- 'dart.assist.addShowCombinator', 30, "Add explicit 'show' combinator");
+ 'dart.assist.add.showCombinator', 30, "Add explicit 'show' combinator");
static const INLINE_INVOCATION =
AssistKind('dart.assist.inline', 30, "Inline invocation of '{0}'");
static const INTRODUCE_LOCAL_CAST_TYPE = AssistKind(
@@ -180,7 +180,7 @@
'dart.assist.joinVariableDeclaration', 30, 'Join variable declaration');
static const REMOVE_TYPE_ANNOTATION = AssistKind(
// todo (pq): unify w/ fix
- 'dart.assist.removeTypeAnnotation',
+ 'dart.assist.remove.typeAnnotation',
29,
'Remove type annotation');
static const REPLACE_CONDITIONAL_WITH_IF_ELSE = AssistKind(
@@ -192,9 +192,7 @@
30,
"Replace 'if-else' with conditional ('c ? x : y')");
static const REPLACE_WITH_VAR = AssistKind(
- 'dart.assist.convert.replaceWithVar',
- 30,
- "Replace type annotation with 'var'");
+ 'dart.assist.replace.withVar', 30, "Replace type annotation with 'var'");
static const SHADOW_FIELD = AssistKind('dart.assist.shadowField', 30,
'Create a local variable that shadows the field');
static const SORT_CHILD_PROPERTY_LAST = AssistKind(
@@ -224,5 +222,5 @@
static const SURROUND_WITH_WHILE =
AssistKind('dart.assist.surround.while', 24, "Surround with 'while'");
static const USE_CURLY_BRACES =
- AssistKind('USE_CURLY_BRACES', 30, 'Use curly braces');
+ AssistKind('dart.assist.surround.curlyBraces', 30, 'Use curly braces');
}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 8a8e8ec..51a755e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -85,6 +85,9 @@
errorCode.name == LintNames.null_closures ||
errorCode.name == LintNames.prefer_collection_literals ||
errorCode.name == LintNames.prefer_conditional_assignment ||
+ errorCode.name == LintNames.prefer_const_constructors ||
+ errorCode.name ==
+ LintNames.prefer_const_constructors_in_immutables ||
errorCode.name == LintNames.prefer_const_declarations ||
errorCode.name == LintNames.prefer_equal_for_default_values ||
errorCode.name == LintNames.prefer_final_fields ||
@@ -103,7 +106,8 @@
/// An enumeration of quick fix kinds for the errors found in an analysis
/// options file.
class AnalysisOptionsFixKind {
- static const REMOVE_SETTING = FixKind('REMOVE_SETTING', 50, "Remove '{0}'");
+ static const REMOVE_SETTING =
+ FixKind('analysisOptions.fix.removeSetting', 50, "Remove '{0}'");
}
/// The implementation of [DartFixContext].
@@ -131,295 +135,332 @@
/// An enumeration of quick fix kinds found in a Dart file.
class DartFixKind {
- static const ADD_ASYNC = FixKind('ADD_ASYNC', 50, "Add 'async' modifier");
- static const ADD_AWAIT = FixKind('ADD_AWAIT', 50, "Add 'await' keyword");
- static const ADD_EXPLICIT_CAST = FixKind('ADD_EXPLICIT_CAST', 50, 'Add cast',
+ static const ADD_ASYNC =
+ FixKind('dart.fix.add.async', 50, "Add 'async' modifier");
+ static const ADD_AWAIT =
+ FixKind('dart.fix.add.await', 50, "Add 'await' keyword");
+ static const ADD_EXPLICIT_CAST = FixKind(
+ 'dart.fix.add.explicitCast', 50, 'Add cast',
appliedTogetherMessage: 'Add all casts in file');
- static const ADD_CONST = FixKind('ADD_CONST', 50, "Add 'const' modifier");
+ static const ADD_CONST =
+ FixKind('dart.fix.add.const', 50, "Add 'const' modifier");
static const ADD_CURLY_BRACES =
- FixKind('ADD_CURLY_BRACES', 50, 'Add curly braces');
+ FixKind('dart.fix.add.curlyBraces', 50, 'Add curly braces');
static const ADD_DIAGNOSTIC_PROPERTY_REFERENCE = FixKind(
- 'ADD_DIAGNOSTIC_PROPERTY_REFERENCE',
+ 'dart.fix.add.diagnosticPropertyReference',
50,
'Add a debug reference to this property');
static const ADD_FIELD_FORMAL_PARAMETERS = FixKind(
- 'ADD_FIELD_FORMAL_PARAMETERS', 70, 'Add final field formal parameters');
- static const ADD_MISSING_ENUM_CASE_CLAUSES =
- FixKind('ADD_MISSING_ENUM_CASE_CLAUSES', 50, 'Add missing case clauses');
- static const ADD_MISSING_PARAMETER_NAMED =
- FixKind('ADD_MISSING_PARAMETER_NAMED', 70, "Add named parameter '{0}'");
+ 'dart.fix.add.fieldFormalParameters',
+ 70,
+ 'Add final field formal parameters');
+ static const ADD_MISSING_ENUM_CASE_CLAUSES = FixKind(
+ 'dart.fix.add.missingEnumCaseClauses', 50, 'Add missing case clauses');
+ static const ADD_MISSING_PARAMETER_NAMED = FixKind(
+ 'dart.fix.add.missingParameterNamed', 70, "Add named parameter '{0}'");
static const ADD_MISSING_PARAMETER_POSITIONAL = FixKind(
- 'ADD_MISSING_PARAMETER_POSITIONAL',
+ 'dart.fix.add.missingParameterPositional',
69,
'Add optional positional parameter');
- static const ADD_MISSING_PARAMETER_REQUIRED =
- FixKind('ADD_MISSING_PARAMETER_REQUIRED', 70, 'Add required parameter');
+ static const ADD_MISSING_PARAMETER_REQUIRED = FixKind(
+ 'dart.fix.add.missingParameterRequired', 70, 'Add required parameter');
static const ADD_MISSING_REQUIRED_ARGUMENT = FixKind(
- 'ADD_MISSING_REQUIRED_ARGUMENT', 70, "Add required argument '{0}'");
- static const ADD_NE_NULL = FixKind('ADD_NE_NULL', 50, 'Add != null',
+ 'dart.fix.add.missingRequiredArgument',
+ 70,
+ "Add required argument '{0}'");
+ static const ADD_NE_NULL = FixKind('dart.fix.add.neNull', 50, 'Add != null',
appliedTogetherMessage: 'Add != null everywhere in file');
static const ADD_OVERRIDE =
- FixKind('ADD_OVERRIDE', 50, "Add '@override' annotation");
+ FixKind('dart.fix.add.override', 50, "Add '@override' annotation");
static const ADD_REQUIRED =
- FixKind('ADD_REQUIRED', 50, "Add '@required' annotation");
+ FixKind('dart.fix.add.required', 50, "Add '@required' annotation");
static const ADD_RETURN_TYPE =
- FixKind('ADD_RETURN_TYPE', 50, 'Add return type');
- static const ADD_STATIC = FixKind('ADD_STATIC', 50, "Add 'static' modifier");
+ FixKind('dart.fix.add.returnType', 50, 'Add return type');
+ static const ADD_STATIC =
+ FixKind('dart.fix.add.static', 50, "Add 'static' modifier");
static const ADD_SUPER_CONSTRUCTOR_INVOCATION = FixKind(
- 'ADD_SUPER_CONSTRUCTOR_INVOCATION',
+ 'dart.fix.add.superConstructorInvocation',
50,
'Add super constructor {0} invocation');
static const ADD_TYPE_ANNOTATION =
- FixKind('ADD_TYPE_ANNOTATION', 50, 'Add type annotation');
+ FixKind('dart.fix.add.typeAnnotation', 50, 'Add type annotation');
static const CHANGE_ARGUMENT_NAME =
- FixKind('CHANGE_ARGUMENT_NAME', 60, "Change to '{0}'");
- static const CHANGE_TO = FixKind('CHANGE_TO', 51, "Change to '{0}'");
+ FixKind('dart.fix.change.argumentName', 60, "Change to '{0}'");
+ static const CHANGE_TO = FixKind('dart.fix.change.to', 51, "Change to '{0}'");
static const CHANGE_TO_NEAREST_PRECISE_VALUE = FixKind(
- 'CHANGE_TO_NEAREST_PRECISE_VALUE',
+ 'dart.fix.change.toNearestPreciseValue',
50,
'Change to nearest precise int-as-double value: {0}');
static const CHANGE_TO_STATIC_ACCESS = FixKind(
- 'CHANGE_TO_STATIC_ACCESS', 50, "Change access to static using '{0}'");
+ 'dart.fix.change.toStaticAccess',
+ 50,
+ "Change access to static using '{0}'");
static const CHANGE_TYPE_ANNOTATION = FixKind(
- 'CHANGE_TYPE_ANNOTATION', 50, "Change '{0}' to '{1}' type annotation");
- static const CONVERT_FLUTTER_CHILD =
- FixKind('CONVERT_FLUTTER_CHILD', 50, 'Convert to children:');
- static const CONVERT_FLUTTER_CHILDREN =
- FixKind('CONVERT_FLUTTER_CHILDREN', 50, 'Convert to child:');
- static const CONVERT_INTO_EXPRESSION_BODY =
- FixKind('CONVERT_INTO_EXPRESSION_BODY', 50, 'Convert to expression body');
+ 'dart.fix.change.typeAnnotation',
+ 50,
+ "Change '{0}' to '{1}' type annotation");
+ static const CONVERT_FLUTTER_CHILD = FixKind(
+ 'dart.fix.flutter.convert.childToChildren', 50, 'Convert to children:');
+ static const CONVERT_FLUTTER_CHILDREN = FixKind(
+ 'dart.fix.flutter.convert.childrenToChild', 50, 'Convert to child:');
+ static const CONVERT_INTO_EXPRESSION_BODY = FixKind(
+ 'dart.fix.convert.toExpressionBody', 50, 'Convert to expression body');
static const CONVERT_TO_CONTAINS =
- FixKind('CONVERT_TO_CONTAINS', 50, "Convert to using 'contains'");
- static const CONVERT_TO_FOR_ELEMENT =
- FixKind('CONVERT_TO_FOR_ELEMENT', 50, "Convert to a 'for' element");
+ FixKind('dart.fix.convert.toContains', 50, "Convert to using 'contains'");
+ static const CONVERT_TO_FOR_ELEMENT = FixKind(
+ 'dart.fix.convert.toForElement', 50, "Convert to a 'for' element");
static const CONVERT_TO_GENERIC_FUNCTION_SYNTAX = FixKind(
- 'CONVERT_TO_GENERIC_FUNCTION_SYNTAX',
+ 'dart.fix.convert.toGenericFunctionSyntax',
50,
"Convert into 'Function' syntax");
static const CONVERT_TO_IF_ELEMENT =
- FixKind('CONVERT_TO_IF_ELEMENT', 50, "Convert to an 'if' element");
+ FixKind('dart.fix.convert.toIfElement', 50, "Convert to an 'if' element");
static const CONVERT_TO_IF_NULL =
- FixKind('CONVERT_TO_IF_NULL', 50, "Convert to use '??'");
+ FixKind('dart.fix.convert.toIfNull', 50, "Convert to use '??'");
static const CONVERT_TO_INT_LITERAL =
- FixKind('CONVERT_TO_INT_LITERAL', 50, 'Convert to an int literal');
+ FixKind('dart.fix.convert.toIntLiteral', 50, 'Convert to an int literal');
static const CONVERT_TO_LINE_COMMENT = FixKind(
- 'CONVERT_TO_LINE_COMMENT', 50, 'Convert to line documentation comment');
+ 'dart.fix.convert.toLineComment',
+ 50,
+ 'Convert to line documentation comment');
static const CONVERT_TO_LIST_LITERAL =
- FixKind('CONVERT_TO_LIST_LITERAL', 50, 'Convert to list literal');
+ FixKind('dart.fix.convert.toListLiteral', 50, 'Convert to list literal');
static const CONVERT_TO_MAP_LITERAL =
- FixKind('CONVERT_TO_MAP_LITERAL', 50, 'Convert to map literal');
- static const CONVERT_TO_NAMED_ARGUMENTS =
- FixKind('CONVERT_TO_NAMED_ARGUMENTS', 50, 'Convert to named arguments');
+ FixKind('dart.fix.convert.toMapLiteral', 50, 'Convert to map literal');
+ static const CONVERT_TO_NAMED_ARGUMENTS = FixKind(
+ 'dart.fix.convert.toNamedArguments', 50, 'Convert to named arguments');
static const CONVERT_TO_NULL_AWARE =
- FixKind('CONVERT_TO_NULL_AWARE', 50, "Convert to use '?.'");
- static const CONVERT_TO_PACKAGE_IMPORT =
- FixKind('CONVERT_TO_PACKAGE_IMPORT', 50, "Convert to 'package:' import");
- static const CONVERT_TO_RELATIVE_IMPORT =
- FixKind('CONVERT_TO_RELATIVE_IMPORT', 50, 'Convert to relative import');
+ FixKind('dart.fix.convert.toNullAware', 50, "Convert to use '?.'");
+ static const CONVERT_TO_PACKAGE_IMPORT = FixKind(
+ 'dart.fix.convert.toPackageImport', 50, "Convert to 'package:' import");
+ static const CONVERT_TO_RELATIVE_IMPORT = FixKind(
+ 'dart.fix.convert.toRelativeImport', 50, 'Convert to relative import');
static const CONVERT_TO_SET_LITERAL =
- FixKind('CONVERT_TO_SET_LITERAL', 50, 'Convert to set literal');
+ FixKind('dart.fix.convert.toSetLiteral', 50, 'Convert to set literal');
static const CONVERT_TO_SINGLE_QUOTED_STRING = FixKind(
- 'CONVERT_TO_SINGLE_QUOTED_STRING', 50, 'Convert to single quoted string');
+ 'dart.fix.convert.toSingleQuotedString',
+ 50,
+ 'Convert to single quoted string');
static const CONVERT_TO_SPREAD =
- FixKind('CONVERT_TO_SPREAD', 50, 'Convert to a spread');
- static const CONVERT_TO_WHERE_TYPE =
- FixKind('CONVERT_TO_WHERE_TYPE', 50, "Convert to a use 'whereType'");
- static const CREATE_CLASS = FixKind('CREATE_CLASS', 50, "Create class '{0}'");
+ FixKind('dart.fix.convert.toSpread', 50, 'Convert to a spread');
+ static const CONVERT_TO_WHERE_TYPE = FixKind(
+ 'dart.fix.convert.toWhereType', 50, "Convert to a use 'whereType'");
+ static const CREATE_CLASS =
+ FixKind('dart.fix.create.class', 50, "Create class '{0}'");
static const CREATE_CONSTRUCTOR =
- FixKind('CREATE_CONSTRUCTOR', 50, "Create constructor '{0}'");
+ FixKind('dart.fix.create.constructor', 50, "Create constructor '{0}'");
static const CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS = FixKind(
- 'CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS',
+ 'dart.fix.create.constructorForFinalFields',
50,
'Create constructor for final fields');
- static const CREATE_CONSTRUCTOR_SUPER =
- FixKind('CREATE_CONSTRUCTOR_SUPER', 50, 'Create constructor to call {0}');
- static const CREATE_FIELD = FixKind('CREATE_FIELD', 49, "Create field '{0}'");
- static const CREATE_FILE = FixKind('CREATE_FILE', 50, "Create file '{0}'");
+ static const CREATE_CONSTRUCTOR_SUPER = FixKind(
+ 'dart.fix.create.constructorSuper', 50, 'Create constructor to call {0}');
+ static const CREATE_FIELD =
+ FixKind('dart.fix.create.field', 49, "Create field '{0}'");
+ static const CREATE_FILE =
+ FixKind('dart.fix.create.file', 50, "Create file '{0}'");
static const CREATE_FUNCTION =
- FixKind('CREATE_FUNCTION', 49, "Create function '{0}'");
+ FixKind('dart.fix.create.function', 49, "Create function '{0}'");
static const CREATE_GETTER =
- FixKind('CREATE_GETTER', 50, "Create getter '{0}'");
- static const CREATE_LOCAL_VARIABLE =
- FixKind('CREATE_LOCAL_VARIABLE', 50, "Create local variable '{0}'");
+ FixKind('dart.fix.create.getter', 50, "Create getter '{0}'");
+ static const CREATE_LOCAL_VARIABLE = FixKind(
+ 'dart.fix.create.localVariable', 50, "Create local variable '{0}'");
static const CREATE_METHOD =
- FixKind('CREATE_METHOD', 50, "Create method '{0}'");
- static const CREATE_MISSING_OVERRIDES =
- FixKind('CREATE_MISSING_OVERRIDES', 51, 'Create {0} missing override(s)');
- static const CREATE_MIXIN = FixKind('CREATE_MIXIN', 50, "Create mixin '{0}'");
- static const CREATE_NO_SUCH_METHOD =
- FixKind('CREATE_NO_SUCH_METHOD', 49, "Create 'noSuchMethod' method");
+ FixKind('dart.fix.create.method', 50, "Create method '{0}'");
+ static const CREATE_MISSING_OVERRIDES = FixKind(
+ 'dart.fix.create.missingOverrides', 51, 'Create {0} missing override(s)');
+ static const CREATE_MIXIN =
+ FixKind('dart.fix.create.mixin', 50, "Create mixin '{0}'");
+ static const CREATE_NO_SUCH_METHOD = FixKind(
+ 'dart.fix.create.noSuchMethod', 49, "Create 'noSuchMethod' method");
static const CREATE_SETTER =
- FixKind('CREATE_SETTER', 50, "Create setter '{0}'");
+ FixKind('dart.fix.create.setter', 50, "Create setter '{0}'");
static const EXTEND_CLASS_FOR_MIXIN =
- FixKind('EXTEND_CLASS_FOR_MIXIN', 50, "Extend the class '{0}'");
+ FixKind('dart.fix.extendClassForMixin', 50, "Extend the class '{0}'");
static const IMPORT_ASYNC =
- FixKind('IMPORT_ASYNC', 49, "Import 'dart:async'");
- static const IMPORT_LIBRARY_PREFIX = FixKind('IMPORT_LIBRARY_PREFIX', 49,
- "Use imported library '{0}' with prefix '{1}'");
+ FixKind('dart.fix.import.async', 49, "Import 'dart:async'");
+ static const IMPORT_LIBRARY_PREFIX = FixKind('dart.fix.import.libraryPrefix',
+ 49, "Use imported library '{0}' with prefix '{1}'");
static const IMPORT_LIBRARY_PROJECT1 =
- FixKind('IMPORT_LIBRARY_PROJECT1', 53, "Import library '{0}'");
+ FixKind('dart.fix.import.libraryProject1', 53, "Import library '{0}'");
static const IMPORT_LIBRARY_PROJECT2 =
- FixKind('IMPORT_LIBRARY_PROJECT2', 52, "Import library '{0}'");
+ FixKind('dart.fix.import.libraryProject2', 52, "Import library '{0}'");
static const IMPORT_LIBRARY_PROJECT3 =
- FixKind('IMPORT_LIBRARY_PROJECT3', 51, "Import library '{0}'");
+ FixKind('dart.fix.import.libraryProject3', 51, "Import library '{0}'");
static const IMPORT_LIBRARY_SDK =
- FixKind('IMPORT_LIBRARY_SDK', 54, "Import library '{0}'");
+ FixKind('dart.fix.import.librarySdk', 54, "Import library '{0}'");
static const IMPORT_LIBRARY_SHOW =
- FixKind('IMPORT_LIBRARY_SHOW', 55, "Update library '{0}' import");
+ FixKind('dart.fix.import.libraryShow', 55, "Update library '{0}' import");
static const INLINE_INVOCATION =
- FixKind('INLINE_INVOCATION', 30, "Inline invocation of '{0}'");
+ FixKind('dart.fix.inlineInvocation', 30, "Inline invocation of '{0}'");
static const INLINE_TYPEDEF =
- FixKind('INLINE_TYPEDEF', 30, "Inline the definition of '{0}'");
- static const INSERT_SEMICOLON = FixKind('INSERT_SEMICOLON', 50, "Insert ';'");
+ FixKind('dart.fix.inlineTypedef', 30, "Inline the definition of '{0}'");
+ static const INSERT_SEMICOLON =
+ FixKind('dart.fix.insertSemicolon', 50, "Insert ';'");
static const MAKE_CLASS_ABSTRACT =
- FixKind('MAKE_CLASS_ABSTRACT', 50, "Make class '{0}' abstract");
+ FixKind('dart.fix.makeClassAbstract', 50, "Make class '{0}' abstract");
static const MAKE_FIELD_NOT_FINAL =
- FixKind('MAKE_FIELD_NOT_FINAL', 50, "Make field '{0}' not final");
- static const MAKE_FINAL = FixKind('MAKE_FINAL', 50, 'Make final');
+ FixKind('dart.fix.makeFieldNotFinal', 50, "Make field '{0}' not final");
+ static const MAKE_FINAL = FixKind('dart.fix.makeFinal', 50, 'Make final');
static const MOVE_TYPE_ARGUMENTS_TO_CLASS = FixKind(
- 'MOVE_TYPE_ARGUMENTS_TO_CLASS',
+ 'dart.fix.moveTypeArgumentsToClass',
50,
'Move type arguments to after class name');
- static const MAKE_VARIABLE_NOT_FINAL =
- FixKind('MAKE_VARIABLE_NOT_FINAL', 50, "Make variable '{0}' not final");
+ static const MAKE_VARIABLE_NOT_FINAL = FixKind(
+ 'dart.fix.makeVariableNotFinal', 50, "Make variable '{0}' not final");
static const QUALIFY_REFERENCE =
- FixKind('QUALIFY_REFERENCE', 50, "Use '{0}'");
+ FixKind('dart.fix.qualifyReference', 50, "Use '{0}'");
static const REMOVE_ANNOTATION =
- FixKind('REMOVE_ANNOTATION', 50, "Remove the '{0}' annotation");
+ FixKind('dart.fix.remove.annotation', 50, "Remove the '{0}' annotation");
static const REMOVE_ARGUMENT =
- FixKind('REMOVE_ARGUMENT', 50, 'Remove argument');
- static const REMOVE_AWAIT = FixKind('REMOVE_AWAIT', 50, 'Remove await');
- static const REMOVE_CONST = FixKind('REMOVE_CONST', 50, 'Remove const');
+ FixKind('dart.fix.remove.argument', 50, 'Remove argument');
+ static const REMOVE_AWAIT =
+ FixKind('dart.fix.remove.await', 50, 'Remove await');
+ static const REMOVE_CONST =
+ FixKind('dart.fix.remove.const', 50, 'Remove const');
static const REMOVE_DEAD_CODE =
- FixKind('REMOVE_DEAD_CODE', 50, 'Remove dead code');
- static const REMOVE_DUPLICATE_CASE =
- FixKind('REMOVE_DUPLICATE_CASE', 50, 'Remove duplicate case statement');
+ FixKind('dart.fix.remove.deadCode', 50, 'Remove dead code');
+ static const REMOVE_DUPLICATE_CASE = FixKind(
+ 'dart.fix.remove.duplicateCase', 50, 'Remove duplicate case statement');
static const REMOVE_EMPTY_CATCH =
- FixKind('REMOVE_EMPTY_CATCH', 50, 'Remove empty catch clause');
+ FixKind('dart.fix.remove.emptyCatch', 50, 'Remove empty catch clause');
static const REMOVE_EMPTY_CONSTRUCTOR_BODY = FixKind(
- 'REMOVE_EMPTY_CONSTRUCTOR_BODY', 50, 'Remove empty constructor body');
+ 'dart.fix.remove.emptyConstructorBody',
+ 50,
+ 'Remove empty constructor body');
static const REMOVE_EMPTY_ELSE =
- FixKind('REMOVE_EMPTY_ELSE', 50, 'Remove empty else clause');
+ FixKind('dart.fix.remove.emptyElse', 50, 'Remove empty else clause');
static const REMOVE_EMPTY_STATEMENT =
- FixKind('REMOVE_EMPTY_STATEMENT', 50, 'Remove empty statement');
+ FixKind('dart.fix.remove.emptyStatement', 50, 'Remove empty statement');
static const REMOVE_IF_NULL_OPERATOR =
- FixKind('REMOVE_IF_NULL_OPERATOR', 50, "Remove the '??' operator");
+ FixKind('dart.fix.remove.ifNullOperator', 50, "Remove the '??' operator");
static const REMOVE_INITIALIZER =
- FixKind('REMOVE_INITIALIZER', 50, 'Remove initializer');
+ FixKind('dart.fix.remove.initializer', 50, 'Remove initializer');
static const REMOVE_INTERPOLATION_BRACES = FixKind(
- 'REMOVE_INTERPOLATION_BRACES',
+ 'dart.fix.remove.interpolationBraces',
50,
'Remove unnecessary interpolation braces');
- static const REMOVE_METHOD_DECLARATION =
- FixKind('REMOVE_METHOD_DECLARATION', 50, 'Remove method declaration');
- static const REMOVE_NAME_FROM_COMBINATOR =
- FixKind('REMOVE_NAME_FROM_COMBINATOR', 50, "Remove name from '{0}'");
+ static const REMOVE_METHOD_DECLARATION = FixKind(
+ 'dart.fix.remove.methodDeclaration', 50, 'Remove method declaration');
+ static const REMOVE_NAME_FROM_COMBINATOR = FixKind(
+ 'dart.fix.remove.nameFromCombinator', 50, "Remove name from '{0}'");
static const REMOVE_OPERATOR =
- FixKind('REMOVE_OPERATOR', 50, 'Remove the operator');
+ FixKind('dart.fix.remove.operator', 50, 'Remove the operator');
static const REMOVE_PARAMETERS_IN_GETTER_DECLARATION = FixKind(
- 'REMOVE_PARAMETERS_IN_GETTER_DECLARATION',
+ 'dart.fix.remove.parametersInGetterDeclaration',
50,
'Remove parameters in getter declaration');
static const REMOVE_PARENTHESIS_IN_GETTER_INVOCATION = FixKind(
- 'REMOVE_PARENTHESIS_IN_GETTER_INVOCATION',
+ 'dart.fix.remove.parenthesisInGetterInvocation',
50,
'Remove parentheses in getter invocation');
static const REMOVE_THIS_EXPRESSION =
- FixKind('REMOVE_THIS_EXPRESSION', 50, 'Remove this expression');
+ FixKind('dart.fix.remove.thisExpression', 50, 'Remove this expression');
static const REMOVE_TYPE_ANNOTATION =
- FixKind('REMOVE_TYPE_ANNOTATION', 50, 'Remove type annotation');
+ FixKind('dart.fix.remove.typeAnnotation', 50, 'Remove type annotation');
static const REMOVE_TYPE_ARGUMENTS =
- FixKind('REMOVE_TYPE_ARGUMENTS', 49, 'Remove type arguments');
+ FixKind('dart.fix.remove.typeArguments', 49, 'Remove type arguments');
static const REMOVE_UNNECESSARY_CAST = FixKind(
- 'REMOVE_UNNECESSARY_CAST', 50, 'Remove unnecessary cast',
+ 'dart.fix.remove.unnecessaryCast', 50, 'Remove unnecessary cast',
appliedTogetherMessage: 'Remove all unnecessary casts in file');
static const REMOVE_UNNECESSARY_CONST = FixKind(
- 'REMOVE_UNNECESSARY_CONST', 50, 'Remove unnecessary const keyword');
- static const REMOVE_UNNECESSARY_NEW =
- FixKind('REMOVE_UNNECESSARY_NEW', 50, 'Remove unnecessary new keyword');
- static const REMOVE_UNUSED_CATCH_CLAUSE =
- FixKind('REMOVE_UNUSED_CATCH_CLAUSE', 50, "Remove unused 'catch' clause");
+ 'dart.fix.remove.unnecessaryConst',
+ 50,
+ 'Remove unnecessary const keyword');
+ static const REMOVE_UNNECESSARY_NEW = FixKind(
+ 'dart.fix.remove.unnecessaryNew', 50, 'Remove unnecessary new keyword');
+ static const REMOVE_UNUSED_CATCH_CLAUSE = FixKind(
+ 'dart.fix.remove.unusedCatchClause', 50, "Remove unused 'catch' clause");
static const REMOVE_UNUSED_CATCH_STACK = FixKind(
- 'REMOVE_UNUSED_CATCH_STACK', 50, 'Remove unused stack trace variable');
+ 'dart.fix.remove.unusedCatchStack',
+ 50,
+ 'Remove unused stack trace variable');
static const REMOVE_UNUSED_ELEMENT =
- FixKind('REMOVE_UNUSED_ELEMENT', 50, 'Remove unused element');
+ FixKind('dart.fix.remove.unusedElement', 50, 'Remove unused element');
static const REMOVE_UNUSED_FIELD =
- FixKind('REMOVE_UNUSED_FIELD', 50, 'Remove unused field');
+ FixKind('dart.fix.remove.unusedField', 50, 'Remove unused field');
static const REMOVE_UNUSED_IMPORT = FixKind(
- 'REMOVE_UNUSED_IMPORT', 50, 'Remove unused import',
+ 'dart.fix.remove.unusedImport', 50, 'Remove unused import',
appliedTogetherMessage: 'Remove all unused imports in this file');
static const REMOVE_UNUSED_LABEL =
- FixKind('REMOVE_UNUSED_LABEL', 50, 'Remove unused label');
+ FixKind('dart.fix.remove.unusedLabel', 50, 'Remove unused label');
static const REMOVE_UNUSED_LOCAL_VARIABLE = FixKind(
- 'REMOVE_UNUSED_LOCAL_VARIABLE', 50, 'Remove unused local variable');
+ 'dart.fix.remove.unusedLocalVariable',
+ 50,
+ 'Remove unused local variable');
static const RENAME_TO_CAMEL_CASE =
- FixKind('RENAME_TO_CAMEL_CASE', 50, "Rename to '{0}'");
+ FixKind('dart.fix.rename.toCamelCase', 50, "Rename to '{0}'");
static const REPLACE_BOOLEAN_WITH_BOOL = FixKind(
- 'REPLACE_BOOLEAN_WITH_BOOL', 50, "Replace 'boolean' with 'bool'",
+ 'dart.fix.replace.booleanWithBool', 50, "Replace 'boolean' with 'bool'",
appliedTogetherMessage: "Replace all 'boolean' with 'bool' in file");
static const REPLACE_COLON_WITH_EQUALS =
- FixKind('REPLACE_COLON_WITH_EQUALS', 50, "Replace ':' with '='");
- static const REPLACE_FINAL_WITH_CONST =
- FixKind('REPLACE_FINAL_WITH_CONST', 50, "Replace 'final' with 'const'");
- static const REPLACE_NEW_WITH_CONST =
- FixKind('REPLACE_NEW_WITH_CONST', 50, "Replace 'new' with 'const'");
- static const REPLACE_NULL_WITH_CLOSURE =
- FixKind('REPLACE_NULL_WITH_CLOSURE', 50, "Replace 'null' with a closure");
+ FixKind('dart.fix.replace.colonWithEquals', 50, "Replace ':' with '='");
+ static const REPLACE_FINAL_WITH_CONST = FixKind(
+ 'dart.fix.replace.finalWithConst', 50, "Replace 'final' with 'const'");
+ static const REPLACE_NEW_WITH_CONST = FixKind(
+ 'dart.fix.replace.newWithConst', 50, "Replace 'new' with 'const'");
+ static const REPLACE_NULL_WITH_CLOSURE = FixKind(
+ 'dart.fix.replace.nullWithClosure', 50, "Replace 'null' with a closure");
static const REPLACE_RETURN_TYPE_FUTURE = FixKind(
- 'REPLACE_RETURN_TYPE_FUTURE',
+ 'dart.fix.replace.returnTypeFuture',
50,
"Return 'Future' from 'async' function");
- static const REPLACE_VAR_WITH_DYNAMIC =
- FixKind('REPLACE_VAR_WITH_DYNAMIC', 50, "Replace 'var' with 'dynamic'");
+ static const REPLACE_VAR_WITH_DYNAMIC = FixKind(
+ 'dart.fix.replace.varWithDynamic', 50, "Replace 'var' with 'dynamic'");
static const REPLACE_WITH_EIGHT_DIGIT_HEX =
- FixKind('REPLACE_WITH_EIGHT_DIGIT_HEX', 50, "Replace with '{0}'");
+ FixKind('dart.fix.replace.withEightDigitHex', 50, "Replace with '{0}'");
static const REPLACE_WITH_BRACKETS =
- FixKind('REPLACE_WITH_BRACKETS', 50, 'Replace with { }');
- static const REPLACE_WITH_CONDITIONAL_ASSIGNMENT =
- FixKind('REPLACE_WITH_CONDITIONAL_ASSIGNMENT', 50, 'Replace with ??=');
+ FixKind('dart.fix.replace.withBrackets', 50, 'Replace with { }');
+ static const REPLACE_WITH_CONDITIONAL_ASSIGNMENT = FixKind(
+ 'dart.fix.replace.withConditionalAssignment', 50, 'Replace with ??=');
static const REPLACE_WITH_EXTENSION_NAME =
- FixKind('REPLACE_WITH_EXTENSION_NAME', 50, "Replace with '{0}'");
+ FixKind('dart.fix.replace.withExtensionName', 50, "Replace with '{0}'");
static const REPLACE_WITH_IDENTIFIER =
- FixKind('REPLACE_WITH_IDENTIFIER', 50, 'Replace with identifier');
- static const REPLACE_WITH_INTERPOLATION =
- FixKind('REPLACE_WITH_INTERPOLATION', 50, 'Replace with interpolation');
+ FixKind('dart.fix.replace.withIdentifier', 50, 'Replace with identifier');
+ static const REPLACE_WITH_INTERPOLATION = FixKind(
+ 'dart.fix.replace.withInterpolation', 50, 'Replace with interpolation');
static const REPLACE_WITH_IS_EMPTY =
- FixKind('REPLACE_WITH_IS_EMPTY', 50, "Replace with 'isEmpty'");
- static const REPLACE_WITH_IS_NOT_EMPTY =
- FixKind('REPLACE_WITH_IS_NOT_EMPTY', 50, "Replace with 'isNotEmpty'");
- static const REPLACE_WITH_NULL_AWARE = FixKind('REPLACE_WITH_NULL_AWARE', 50,
+ FixKind('dart.fix.replace.withIsEmpty', 50, "Replace with 'isEmpty'");
+ static const REPLACE_WITH_IS_NOT_EMPTY = FixKind(
+ 'dart.fix.replace.withIsNotEmpty', 50, "Replace with 'isNotEmpty'");
+ static const REPLACE_WITH_NULL_AWARE = FixKind(
+ 'dart.fix.replace.withNullAware',
+ 50,
"Replace the '.' with a '?.' in the invocation");
- static const REPLACE_WITH_TEAR_OFF = FixKind(
- 'REPLACE_WITH_TEAR_OFF', 50, 'Replace function literal with tear-off');
- static const REPLACE_WITH_VAR =
- FixKind('REPLACE_WITH_VAR', 50, "Replace type annotation with 'var'");
- static const SORT_CHILD_PROPERTY_LAST = FixKind('SORT_CHILD_PROPERTY_LAST',
- 50, 'Move child property to end of arguments');
+ static const REPLACE_WITH_TEAR_OFF = FixKind('dart.fix.replace.withTearOff',
+ 50, 'Replace function literal with tear-off');
+ static const REPLACE_WITH_VAR = FixKind(
+ 'dart.fix.replace.withVar', 50, "Replace type annotation with 'var'");
+ static const SORT_CHILD_PROPERTY_LAST = FixKind(
+ 'dart.fix.sort.childPropertyLast',
+ 50,
+ 'Move child property to end of arguments');
static const SORT_DIRECTIVES =
- FixKind('SORT_DIRECTIVES', 50, 'Sort directives');
- static const UPDATE_SDK_CONSTRAINTS =
- FixKind('UPDATE_SDK_CONSTRAINTS', 50, 'Update the SDK constraints');
- static const USE_CONST = FixKind('USE_CONST', 50, 'Change to constant');
+ FixKind('dart.fix.sort.directives', 50, 'Sort directives');
+ static const UPDATE_SDK_CONSTRAINTS = FixKind(
+ 'dart.fix.updateSdkConstraints', 50, 'Update the SDK constraints');
+ static const USE_CONST =
+ FixKind('dart.fix.use.const', 50, 'Change to constant');
static const USE_EFFECTIVE_INTEGER_DIVISION = FixKind(
- 'USE_EFFECTIVE_INTEGER_DIVISION',
+ 'dart.fix.use.effectiveIntegerDivision',
50,
'Use effective integer division ~/');
static const USE_EQ_EQ_NULL = FixKind(
- 'USE_EQ_EQ_NULL', 50, "Use == null instead of 'is Null'",
+ 'dart.fix.use.eqEqNull', 50, "Use == null instead of 'is Null'",
appliedTogetherMessage:
"Use == null instead of 'is Null' everywhere in file");
- static const USE_IS_NOT_EMPTY = FixKind(
- 'USE_IS_NOT_EMPTY', 50, "Use x.isNotEmpty instead of '!x.isEmpty'");
+ static const USE_IS_NOT_EMPTY = FixKind('dart.fix.use.isNotEmpty', 50,
+ "Use x.isNotEmpty instead of '!x.isEmpty'");
static const USE_NOT_EQ_NULL = FixKind(
- 'USE_NOT_EQ_NULL', 50, "Use != null instead of 'is! Null'",
+ 'dart.fix.use.notEqNull', 50, "Use != null instead of 'is! Null'",
appliedTogetherMessage:
"Use != null instead of 'is! Null' everywhere in file");
static const USE_RETHROW =
- FixKind('USE_RETHROW', 50, 'Replace throw with rethrow');
+ FixKind('dart.fix.use.rethrow', 50, 'Replace throw with rethrow');
static const WRAP_IN_FUTURE =
- FixKind('WRAP_IN_FUTURE', 50, "Wrap in 'Future.value'");
+ FixKind('dart.fix.wrap.future', 50, "Wrap in 'Future.value'");
static const WRAP_IN_TEXT =
- FixKind('WRAP_IN_TEXT', 50, "Wrap in a 'Text' widget");
+ FixKind('dart.fix.flutter.wrap.text', 50, "Wrap in a 'Text' widget");
}
/// An enumeration of quick fix kinds for the errors found in an Android
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 98df9e6..1965a45 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -4470,6 +4470,7 @@
if (change.edits.isEmpty && !importsOnly) {
return;
}
+ change.id = kind.id;
change.message = formatList(kind.message, args);
fixes.add(Fix(kind, change));
}
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index 2ea56a3..b33c8d0 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -8,6 +8,7 @@
analyzer: any
analyzer_plugin: any
args: any
+ charcode: any
convert: any
crypto: any
dart_style: any
@@ -16,6 +17,7 @@
logging: any
meta: any
nnbd_migration: any
+ pub_semver: any
source_span: any
stream_channel: any
package_config: any
diff --git a/pkg/analysis_server/test/domain_edit_dartfix_test.dart b/pkg/analysis_server/test/domain_edit_dartfix_test.dart
index c493bf4..7cff0de 100644
--- a/pkg/analysis_server/test/domain_edit_dartfix_test.dart
+++ b/pkg/analysis_server/test/domain_edit_dartfix_test.dart
@@ -175,72 +175,74 @@
throwsA(TypeMatcher<StateError>()));
}
- Future<void> test_nonNullable_analysisOptions_created() async {
- // Add pubspec for nnbd migration to detect
- newFile('/project/pubspec.yaml', content: '''
-name: testnnbd
-''');
+ Future<void> test_nonNullable_pubspec_environmentAdded() async {
+ var originalContent = '''
+name: foo
+''';
+ newFile('/project/pubspec.yaml', content: originalContent);
createProject();
var result = await performFix(includedFixes: ['non-nullable']);
expect(result.suggestions.length, greaterThanOrEqualTo(1));
expect(result.hasErrors, isFalse);
expect(result.edits, hasLength(1));
- expectFileEdits('', result.edits[0], '''
-analyzer:
- enable-experiment:
- - non-nullable
+ expectFileEdits(originalContent, result.edits[0], '''
+environment:
+ sdk: '>=2.8.0 <3.0.0'
+name: foo
''');
}
- Future<void> test_nonNullable_analysisOptions_experimentsAdded() async {
- var originalOptions = '''
-analyzer:
- something:
- - other
-
-linter:
- - boo
+ Future<void> test_nonNullable_pubspec_sdkAdded() async {
+ var originalContent = '''
+name: foo
+environment:
+ x: y
''';
- newFile('/project/analysis_options.yaml', content: originalOptions);
+ newFile('/project/pubspec.yaml', content: originalContent);
createProject();
var result = await performFix(includedFixes: ['non-nullable']);
expect(result.suggestions.length, greaterThanOrEqualTo(1));
expect(result.hasErrors, isFalse);
expect(result.edits, hasLength(1));
- expectFileEdits(originalOptions, result.edits[0], '''
-analyzer:
- something:
- - other
- enable-experiment:
- - non-nullable
-
-linter:
- - boo
+ expectFileEdits(originalContent, result.edits[0], '''
+name: foo
+environment:
+ x: y
+ sdk: '>=2.8.0 <3.0.0'
''');
}
- Future<void> test_nonNullable_analysisOptions_nnbdAdded() async {
- var originalOptions = '''
-analyzer:
- enable-experiment:
- - other
-linter:
- - boo
+ Future<void> test_nonNullable_pubspec_sdkNotUpdated() async {
+ var originalContent = '''
+name: foo
+environment:
+ sdk: '>=2.8.0 <3.0.0'
''';
- newFile('/project/analysis_options.yaml', content: originalOptions);
+ newFile('/project/pubspec.yaml', content: originalContent);
+ createProject();
+ var result = await performFix(includedFixes: ['non-nullable']);
+ expect(result.suggestions, isEmpty);
+ expect(result.hasErrors, isFalse);
+ expect(result.edits, isEmpty);
+ }
+
+ Future<void> test_nonNullable_pubspec_sdkUpdated() async {
+ var originalContent = '''
+name: foo
+environment:
+ sdk: '>=2.7.0 <3.0.0'
+''';
+ newFile('/project/pubspec.yaml', content: originalContent);
createProject();
var result = await performFix(includedFixes: ['non-nullable']);
expect(result.suggestions.length, greaterThanOrEqualTo(1));
expect(result.hasErrors, isFalse);
expect(result.edits, hasLength(1));
- expectFileEdits(originalOptions, result.edits[0], '''
-analyzer:
- enable-experiment:
- - other
- - non-nullable
-linter:
- - boo
+ expectFileEdits(originalContent, result.edits[0], '''
+name: foo
+environment:
+ sdk: '>=2.8.0 <3.0.0'
''');
}
diff --git a/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart b/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart
index 15e904c..c46ba90 100644
--- a/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart
+++ b/pkg/analysis_server/test/services/completion/dart/relevance/completion_relevance.dart
@@ -28,7 +28,19 @@
var previous = suggestions[0];
for (var i = 1; i < length; i++) {
var current = suggestions[i];
- expect(current.relevance, lessThan(previous.relevance));
+ if (current.relevance >= previous.relevance) {
+ suggestions.sort(
+ (first, second) => second.relevance.compareTo(first.relevance));
+ var buffer = StringBuffer();
+ buffer.write('Suggestions are not in the expected order. ');
+ buffer.writeln('To accept the current state, use');
+ buffer.writeln();
+ for (var suggestion in suggestions) {
+ var completion = suggestion.completion;
+ buffer.writeln(" suggestionWith(completion: '$completion'),");
+ }
+ fail(buffer.toString());
+ }
previous = current;
}
}
diff --git a/pkg/analysis_server/test/services/completion/dart/relevance/static_member_relevance_test.dart b/pkg/analysis_server/test/services/completion/dart/relevance/static_member_relevance_test.dart
index e16d539..d3a952e 100644
--- a/pkg/analysis_server/test/services/completion/dart/relevance/static_member_relevance_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/relevance/static_member_relevance_test.dart
@@ -41,6 +41,27 @@
]);
}
+ Future<void> test_elementKind() async {
+ await addTestFile('''
+class C {
+ static int f = 0;
+ static void get g {}
+ static set s(int x) {}
+ static void m() {}
+}
+
+void g() {
+ C.^
+}
+''');
+ assertOrder([
+ suggestionWith(completion: 'g'),
+ suggestionWith(completion: 's'),
+ suggestionWith(completion: 'm'),
+ suggestionWith(completion: 'f'),
+ ]);
+ }
+
Future<void> test_hasDeprecated() async {
await addTestFile('''
class C {
diff --git a/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart b/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart
index 9d4a299..17cb33b 100644
--- a/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart
+++ b/pkg/analysis_server/test/src/edit/nnbd_migration/info_builder_test.dart
@@ -236,7 +236,7 @@
@reflectiveTest
class InfoBuilderTest extends NnbdMigrationTestBase {
/// Assert various properties of the given [edit].
- void assertEdit(
+ bool assertEdit(
{@required EditDetail edit, int offset, int length, String replacement}) {
expect(edit, isNotNull);
if (offset != null) {
@@ -248,6 +248,7 @@
if (replacement != null) {
expect(edit.replacement, replacement);
}
+ return true;
}
void assertTraceEntry(UnitInfo unit, TraceEntryInfo entryInfo,
@@ -270,6 +271,28 @@
.toList();
}
+ Future<void> test_addLate_dueToHint() async {
+ var content = '/*late*/ int x = 0;';
+ var migratedContent = '/*late*/ int x = 0;';
+ var unit = await buildInfoForSingleTestFile(content,
+ migratedContent: migratedContent);
+ var regions = unit.fixRegions;
+ expect(regions, hasLength(2));
+ var textToRemove = '/*late*/ ';
+ assertRegionPair(regions, 0,
+ offset1: migratedContent.indexOf('/*'),
+ length1: 2,
+ offset2: migratedContent.indexOf('*/'),
+ length2: 2,
+ explanation: 'Added a late keyword, due to a hint',
+ kind: NullabilityFixKind.addLateDueToHint,
+ edits: (edits) => assertEdit(
+ edit: edits.single,
+ offset: content.indexOf(textToRemove),
+ length: textToRemove.length,
+ replacement: ''));
+ }
+
Future<void> test_discardCondition() async {
var unit = await buildInfoForSingleTestFile('''
void g(int i) {
@@ -298,7 +321,7 @@
Future<void> test_downcast_nonNullable() async {
var content = 'int/*!*/ f(num/*!*/ n) => n;';
- var migratedContent = 'int /*!*/ f(num /*!*/ n) => n as int;';
+ var migratedContent = 'int/*!*/ f(num/*!*/ n) => n as int;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
var regions = getNonInformativeRegions(unit.regions);
@@ -318,7 +341,7 @@
var content = 'int/*?*/ f(num/*!*/ n) => n;';
// TODO(paulberry): we should actually cast to `int`, not `int?`, because we
// know `n` is non-nullable.
- var migratedContent = 'int?/*?*/ f(num /*!*/ n) => n as int?;';
+ var migratedContent = 'int/*?*/ f(num/*!*/ n) => n as int?;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
var regions = getNonInformativeRegions(unit.regions);
@@ -339,7 +362,7 @@
Future<void> test_downcast_nullable() async {
var content = 'int/*?*/ f(num/*?*/ n) => n;';
- var migratedContent = 'int?/*?*/ f(num?/*?*/ n) => n as int?;';
+ var migratedContent = 'int/*?*/ f(num/*?*/ n) => n as int?;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
var regions = getNonInformativeRegions(unit.regions);
@@ -357,7 +380,7 @@
Future<void> test_downcast_nullable_to_nonNullable() async {
var content = 'int/*!*/ f(num/*?*/ n) => n;';
- var migratedContent = 'int /*!*/ f(num?/*?*/ n) => n as int;';
+ var migratedContent = 'int/*!*/ f(num/*?*/ n) => n as int;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
var regions = getNonInformativeRegions(unit.regions);
@@ -376,7 +399,7 @@
Future<void> test_downcast_with_traces() async {
var content = 'List<int/*!*/>/*!*/ f(List<int/*?*/>/*?*/ x) => x;';
var migratedContent =
- 'List<int /*!*/> /*!*/ f(List<int?/*?*/>?/*?*/ x) => x as List<int>;';
+ 'List<int/*!*/>/*!*/ f(List<int/*?*/>/*?*/ x) => x as List<int>;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
var regions = unit.regions.where(
@@ -517,12 +540,12 @@
class C {
C? operator+(C c) => null;
}
-C /*!*/ _f(C c) => (c + c)!;
+C/*!*/ _f(C c) => (c + c)!;
''';
var unit = await buildInfoForSingleTestFile(originalContent,
migratedContent: migratedContent);
var regions = unit.fixRegions;
- expect(regions, hasLength(2));
+ expect(regions, hasLength(3));
assertRegion(
region: regions[0],
offset: migratedContent.indexOf('? operator'),
@@ -530,6 +553,12 @@
explanation: "Changed type 'C' to be nullable");
assertRegion(
region: regions[1],
+ offset: migratedContent.indexOf('/*!*/'),
+ length: 5,
+ explanation: "Type 'C' was not made nullable due to a hint",
+ kind: NullabilityFixKind.typeNotMadeNullableDueToHint);
+ assertRegion(
+ region: regions[2],
offset: migratedContent.indexOf('!;'),
length: 1,
explanation: 'Added a non-null assertion to nullable expression',
@@ -538,26 +567,27 @@
Future<void> test_nullCheck_dueToHint() async {
var content = 'int f(int/*?*/ x) => x/*!*/;';
- var migratedContent = 'int f(int?/*?*/ x) => x!/*!*/;';
+ var migratedContent = 'int f(int/*?*/ x) => x/*!*/;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
var regions = unit.fixRegions;
- expect(regions, hasLength(2));
- // regions[0] is `int?`.
- var region = regions[1];
- assertRegion(
- region: region,
- offset: migratedContent.indexOf('!/*!*/'),
+ expect(regions, hasLength(4));
+ assertRegionPair(regions, 0,
+ kind: NullabilityFixKind.makeTypeNullableDueToHint);
+ var hintText = '/*!*/';
+ assertRegionPair(regions, 2,
+ offset1: migratedContent.indexOf(hintText),
+ length1: 2,
+ offset2: migratedContent.indexOf(hintText) + 3,
+ length2: 2,
explanation: 'Accepted a null check hint',
- kind: NullabilityFixKind.checkExpressionDueToHint);
- // Note that traces are still included.
- expect(region.traces, isNotEmpty);
- var textToRemove = '/*!*/';
- assertEdit(
- edit: region.edits.single,
- offset: content.indexOf(textToRemove),
- length: textToRemove.length,
- replacement: '');
+ kind: NullabilityFixKind.checkExpressionDueToHint,
+ traces: isNotEmpty,
+ edits: ((edits) => assertEdit(
+ edit: edits.single,
+ offset: content.indexOf(hintText),
+ length: hintText.length,
+ replacement: '')));
}
Future<void> test_nullCheck_onMemberAccess() async {
@@ -642,7 +672,7 @@
// Note: even though `as int` is removed, it still shows up in the
// preview, since we show deleted text.
var migratedContent = '''
-void f(num n, int?/*?*/ i) {
+void f(num n, int/*?*/ i) {
if (n is! int ) return;
print((n as int).isEven);
print(i! + 1);
@@ -651,16 +681,17 @@
var unit = await buildInfoForSingleTestFile(originalContent,
migratedContent: migratedContent, removeViaComments: false);
var regions = unit.fixRegions;
- expect(regions, hasLength(3));
- // regions[0] is the addition of `?` to the type of `i`.
+ expect(regions, hasLength(4));
+ assertRegionPair(regions, 0,
+ kind: NullabilityFixKind.makeTypeNullableDueToHint);
assertRegion(
- region: regions[1],
+ region: regions[2],
offset: migratedContent.indexOf(' as int'),
length: ' as int'.length,
explanation: 'Discarded a downcast that is now unnecessary',
kind: NullabilityFixKind.removeAs);
assertRegion(
- region: regions[2],
+ region: regions[3],
offset: migratedContent.indexOf('! + 1'),
explanation: 'Added a non-null assertion to nullable expression',
kind: NullabilityFixKind.checkExpression);
@@ -718,7 +749,7 @@
if (i == null) return;
}
''', migratedContent: '''
-void f(int /*!*/ i) {
+void f(int/*!*/ i) {
/* if (i == null) return; */
}
''');
@@ -797,7 +828,7 @@
Future<void> test_trace_nullCheck() async {
var unit = await buildInfoForSingleTestFile('int f(int/*?*/ i) => i + 1;',
- migratedContent: 'int f(int?/*?*/ i) => i! + 1;');
+ migratedContent: 'int f(int/*?*/ i) => i! + 1;');
var region = unit.regions
.where((regionInfo) => regionInfo.offset == unit.content.indexOf('! +'))
.single;
@@ -808,12 +839,16 @@
expect(entries, hasLength(2));
// Entry 0 is the nullability of the type of i.
// TODO(paulberry): -1 is a bug.
- assertTraceEntry(unit, entries[0], 'f', unit.content.indexOf('int?') - 1,
- contains('parameter 0 of f'));
+ assertTraceEntry(unit, entries[0], 'f',
+ unit.content.indexOf('int/*?*/') - 1, contains('parameter 0 of f'));
// Entry 1 is the edge from always to the type of i.
// TODO(paulberry): this edge provides no additional useful information and
// shouldn't be included in the trace.
- assertTraceEntry(unit, entries[1], 'f', unit.content.indexOf('int?') - 1,
+ assertTraceEntry(
+ unit,
+ entries[1],
+ 'f',
+ unit.content.indexOf('int/*?*/') - 1,
'explicitly hinted to be nullable');
}
@@ -835,7 +870,7 @@
void g(int i) { // g
f(i); // call f
}
-void h(int?/*?*/ i) {
+void h(int/*?*/ i) {
g(i!);
}
''');
@@ -872,18 +907,18 @@
Future<void> test_trace_nullCheckHint() async {
var unit = await buildInfoForSingleTestFile('int f(int/*?*/ i) => i/*!*/;',
- migratedContent: 'int f(int?/*?*/ i) => i!/*!*/;');
+ migratedContent: 'int f(int/*?*/ i) => i/*!*/;');
var region = unit.regions
.where(
- (regionInfo) => regionInfo.offset == unit.content.indexOf('!/*!*/'))
+ (regionInfo) => regionInfo.offset == unit.content.indexOf('/*!*/'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
expect(trace.description, 'Reason');
expect(trace.entries, hasLength(1));
- // TODO(paulberry): -2 is a bug.
+ // TODO(paulberry): -1 is a bug.
assertTraceEntry(unit, trace.entries.single, 'f',
- unit.content.indexOf('i!/*!*/') - 2, 'Null check hint');
+ unit.content.indexOf('i/*!*/') - 1, 'Null check hint');
}
Future<void> test_trace_substitutionNode() async {
@@ -895,12 +930,12 @@
Map<int, String> x = {};
String/*!*/ y = x[0];
''', migratedContent: '''
-class C<T extends Object /*!*/> {}
+class C<T extends Object/*!*/> {}
-C<int? /*?*/ >? c;
+C<int /*?*/ >? c;
Map<int , String > x = {};
-String /*!*/ y = x[0]!;
+String/*!*/ y = x[0]!;
''');
var region = unit.regions
.where((regionInfo) => regionInfo.offset == unit.content.indexOf('!;'))
@@ -930,32 +965,35 @@
Future<void> test_type_made_nullable_due_to_hint() async {
var content = 'int/*?*/ x = 0;';
- var migratedContent = 'int?/*?*/ x = 0;';
+ var migratedContent = 'int/*?*/ x = 0;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
- var region = unit.fixRegions.single;
- assertRegion(
- region: region,
- offset: migratedContent.indexOf('?/*?*/'),
+ var regions = unit.fixRegions;
+ expect(regions, hasLength(2));
+ var textToRemove = '/*?*/';
+ assertRegionPair(regions, 0,
+ offset1: migratedContent.indexOf(textToRemove),
+ length1: 2,
+ offset2: migratedContent.indexOf(textToRemove) + 3,
+ length2: 2,
explanation:
"Changed type 'int' to be nullable, due to a nullability hint",
- kind: NullabilityFixKind.makeTypeNullableDueToHint);
- // Note that traces are still included.
- expect(region.traces, isNotNull);
- var textToRemove = '/*?*/';
- var edits = region.edits;
- expect(edits, hasLength(2));
- var editsByDescription = {for (var edit in edits) edit.description: edit};
- assertEdit(
- edit: editsByDescription['Add /*!*/ hint'],
- offset: content.indexOf(textToRemove),
- length: textToRemove.length,
- replacement: '/*!*/');
- assertEdit(
- edit: editsByDescription['Remove /*?*/ hint'],
- offset: content.indexOf(textToRemove),
- length: textToRemove.length,
- replacement: '');
+ kind: NullabilityFixKind.makeTypeNullableDueToHint,
+ traces: isNotNull, edits: (List<EditDetail> edits) {
+ expect(edits, hasLength(2));
+ var editsByDescription = {for (var edit in edits) edit.description: edit};
+ assertEdit(
+ edit: editsByDescription['Change to /*!*/ hint'],
+ offset: content.indexOf(textToRemove),
+ length: textToRemove.length,
+ replacement: '/*!*/');
+ assertEdit(
+ edit: editsByDescription['Remove /*?*/ hint'],
+ offset: content.indexOf(textToRemove),
+ length: textToRemove.length,
+ replacement: '');
+ return true;
+ });
}
Future<void> test_type_not_made_nullable() async {
@@ -975,33 +1013,35 @@
Future<void> test_type_not_made_nullable_due_to_hint() async {
var content = 'int/*!*/ i = 0;';
- var migratedContent = 'int /*!*/ i = 0;';
+ var migratedContent = 'int/*!*/ i = 0;';
var unit = await buildInfoForSingleTestFile(content,
migratedContent: migratedContent);
- var region = unit.regions
- .where((regionInfo) =>
- regionInfo.offset == migratedContent.indexOf(' /*!*/ i'))
- .single;
- assertRegion(
- region: region,
- offset: migratedContent.indexOf(' /*!*/ i'),
- explanation: "Type 'int' was not made nullable due to a hint",
- kind: NullabilityFixKind.typeNotMadeNullableDueToHint);
- // Note that traces are still included.
- expect(region.traces, isNotNull);
+ var regions = unit.regions;
+ expect(regions, hasLength(1));
var textToRemove = '/*!*/';
- var edits = region.edits;
- expect(edits, hasLength(2));
- var editsByDescription = {for (var edit in edits) edit.description: edit};
- assertEdit(
- edit: editsByDescription['Add /*?*/ hint'],
- offset: content.indexOf(textToRemove),
- length: textToRemove.length,
- replacement: '/*?*/');
- assertEdit(
- edit: editsByDescription['Remove /*!*/ hint'],
- offset: content.indexOf(textToRemove),
- length: textToRemove.length,
- replacement: '');
+ assertRegion(
+ region: regions[0],
+ offset: migratedContent.indexOf(textToRemove),
+ length: 5,
+ explanation: "Type 'int' was not made nullable due to a hint",
+ kind: NullabilityFixKind.typeNotMadeNullableDueToHint,
+ traces: isNotNull,
+ edits: (List<EditDetail> edits) {
+ expect(edits, hasLength(2));
+ var editsByDescription = {
+ for (var edit in edits) edit.description: edit
+ };
+ assertEdit(
+ edit: editsByDescription['Change to /*?*/ hint'],
+ offset: content.indexOf(textToRemove),
+ length: textToRemove.length,
+ replacement: '/*?*/');
+ assertEdit(
+ edit: editsByDescription['Remove /*!*/ hint'],
+ offset: content.indexOf(textToRemove),
+ length: textToRemove.length,
+ replacement: '');
+ return true;
+ });
}
}
diff --git a/pkg/analysis_server/test/src/edit/nnbd_migration/nnbd_migration_test_base.dart b/pkg/analysis_server/test/src/edit/nnbd_migration/nnbd_migration_test_base.dart
index b3e1c6b..d8a281a 100644
--- a/pkg/analysis_server/test/src/edit/nnbd_migration/nnbd_migration_test_base.dart
+++ b/pkg/analysis_server/test/src/edit/nnbd_migration/nnbd_migration_test_base.dart
@@ -50,7 +50,7 @@
Object explanation = anything,
Object edits = anything,
Object traces = anything,
- NullabilityFixKind kind = NullabilityFixKind.makeTypeNullable}) {
+ Object kind = NullabilityFixKind.makeTypeNullable}) {
if (offset != null) {
expect(region.offset, offset);
expect(region.length, length ?? 1);
@@ -61,6 +61,36 @@
expect(region.traces, traces);
}
+ /// Asserts various properties of the pair of [regions], `regions[index]` and
+ /// `regions[index + 1]`. The expected offsets and lengths are specified
+ /// separately; everything else is asserted using the same matcher.
+ void assertRegionPair(List<RegionInfo> regions, int index,
+ {int offset1,
+ int length1,
+ int offset2,
+ int length2,
+ Object explanation = anything,
+ Object edits = anything,
+ Object traces = anything,
+ Object kind = anything}) {
+ assertRegion(
+ region: regions[index],
+ offset: offset1,
+ length: length1,
+ explanation: explanation,
+ edits: edits,
+ traces: traces,
+ kind: kind);
+ assertRegion(
+ region: regions[index + 1],
+ offset: offset2,
+ length: length2,
+ explanation: explanation,
+ edits: edits,
+ traces: traces,
+ kind: kind);
+ }
+
/// Uses the InfoBuilder to build information for [testFile].
///
/// The information is stored in [infos].
diff --git a/pkg/analysis_server/test/src/edit/nnbd_migration/unit_renderer_test.dart b/pkg/analysis_server/test/src/edit/nnbd_migration/unit_renderer_test.dart
index a861fd6..5ab9abf 100644
--- a/pkg/analysis_server/test/src/edit/nnbd_migration/unit_renderer_test.dart
+++ b/pkg/analysis_server/test/src/edit/nnbd_migration/unit_renderer_test.dart
@@ -77,6 +77,26 @@
equals('Added a non-null assertion to nullable expression'));
}
+ Future<void> test_editList_countsHintAcceptanceSingly() async {
+ await buildInfoForSingleTestFile('int f(int/*?*/ x) => x/*!*/;',
+ migratedContent: 'int f(int/*?*/ x) => x/*!*/;');
+ var output = renderUnits()[0];
+ expect(
+ output.edits.keys,
+ unorderedEquals([
+ '1 null check hint converted to null check',
+ '1 nullability hint converted to ?'
+ ]));
+ }
+
+ Future<void> test_editList_countsHintAcceptanceSingly_late() async {
+ await buildInfoForSingleTestFile('/*late*/ int x = 0;',
+ migratedContent: '/*late*/ int x = 0;');
+ var output = renderUnits()[0];
+ expect(output.edits.keys,
+ unorderedEquals(['1 late hint converted to late keyword']));
+ }
+
Future<void> test_editList_pluralHeader() async {
await buildInfoForSingleTestFile('''
int a = null;
diff --git a/pkg/analyzer/lib/src/summary/format.dart b/pkg/analyzer/lib/src/summary/format.dart
index 0a6a1df..a97325c 100644
--- a/pkg/analyzer/lib/src/summary/format.dart
+++ b/pkg/analyzer/lib/src/summary/format.dart
@@ -16973,10 +16973,18 @@
class LinkedNodeTypeSubstitutionBuilder extends Object
with _LinkedNodeTypeSubstitutionMixin
implements idl.LinkedNodeTypeSubstitution {
+ bool _isLegacy;
List<LinkedNodeTypeBuilder> _typeArguments;
List<int> _typeParameters;
@override
+ bool get isLegacy => _isLegacy ??= false;
+
+ set isLegacy(bool value) {
+ this._isLegacy = value;
+ }
+
+ @override
List<LinkedNodeTypeBuilder> get typeArguments =>
_typeArguments ??= <LinkedNodeTypeBuilder>[];
@@ -16993,8 +17001,11 @@
}
LinkedNodeTypeSubstitutionBuilder(
- {List<LinkedNodeTypeBuilder> typeArguments, List<int> typeParameters})
- : _typeArguments = typeArguments,
+ {bool isLegacy,
+ List<LinkedNodeTypeBuilder> typeArguments,
+ List<int> typeParameters})
+ : _isLegacy = isLegacy,
+ _typeArguments = typeArguments,
_typeParameters = typeParameters;
/// Flush [informative] data recursively.
@@ -17020,6 +17031,7 @@
x?.collectApiSignature(signature);
}
}
+ signature.addBool(this._isLegacy == true);
}
fb.Offset finish(fb.Builder fbBuilder) {
@@ -17033,6 +17045,9 @@
offset_typeParameters = fbBuilder.writeListUint32(_typeParameters);
}
fbBuilder.startTable();
+ if (_isLegacy == true) {
+ fbBuilder.addBool(2, true);
+ }
if (offset_typeArguments != null) {
fbBuilder.addOffset(1, offset_typeArguments);
}
@@ -17061,10 +17076,17 @@
_LinkedNodeTypeSubstitutionImpl(this._bc, this._bcOffset);
+ bool _isLegacy;
List<idl.LinkedNodeType> _typeArguments;
List<int> _typeParameters;
@override
+ bool get isLegacy {
+ _isLegacy ??= const fb.BoolReader().vTableGet(_bc, _bcOffset, 2, false);
+ return _isLegacy;
+ }
+
+ @override
List<idl.LinkedNodeType> get typeArguments {
_typeArguments ??=
const fb.ListReader<idl.LinkedNodeType>(_LinkedNodeTypeReader())
@@ -17085,6 +17107,9 @@
@override
Map<String, Object> toJson() {
Map<String, Object> _result = <String, Object>{};
+ if (isLegacy != false) {
+ _result["isLegacy"] = isLegacy;
+ }
if (typeArguments.isNotEmpty) {
_result["typeArguments"] =
typeArguments.map((_value) => _value.toJson()).toList();
@@ -17097,6 +17122,7 @@
@override
Map<String, Object> toMap() => {
+ "isLegacy": isLegacy,
"typeArguments": typeArguments,
"typeParameters": typeParameters,
};
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index 7f390d5..cfd13d9 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -1219,6 +1219,8 @@
/// Information about a type substitution.
table LinkedNodeTypeSubstitution {
+ isLegacy:bool (id: 2);
+
typeArguments:[LinkedNodeType] (id: 1);
typeParameters:[uint] (id: 0);
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index 2175ef2..6e182ec 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -1839,6 +1839,9 @@
/// Information about a type substitution.
abstract class LinkedNodeTypeSubstitution extends base.SummaryClass {
+ @Id(2)
+ bool get isLegacy;
+
@Id(1)
List<LinkedNodeType> get typeArguments;
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
index 09fd0b0..c36c2ed 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
@@ -107,7 +107,12 @@
var typeArguments = substitutionNode.typeArguments.map(_readType).toList();
var substitution = Substitution.fromPairs(typeParameters, typeArguments);
- return ExecutableMember.from2(element, substitution);
+ var member = ExecutableMember.from2(element, substitution);
+ if (substitutionNode.isLegacy) {
+ member = Member.legacy(member);
+ }
+
+ return member;
}
T _getElement<T extends Element>(int index) {
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
index 4d0c8bf..497e564 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
@@ -1478,6 +1478,7 @@
var elementIndex = _indexOfElement(element.declaration);
var substitution = element.substitution.map;
var substitutionBuilder = LinkedNodeTypeSubstitutionBuilder(
+ isLegacy: element.isLegacy,
typeParameters: substitution.keys.map(_indexOfElement).toList(),
typeArguments: substitution.values.map(_writeType).toList(),
);
diff --git a/pkg/analyzer/test/src/dart/resolution/constant_test.dart b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
index 2ad2114..25c3b3d 100644
--- a/pkg/analyzer/test/src/dart/resolution/constant_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constant_test.dart
@@ -328,6 +328,24 @@
_assertIntValue(c, 42);
}
+ test_topLevelVariable_optOut3() async {
+ newFile('/test/lib/a.dart', content: r'''
+// @dart = 2.7
+const a = int.fromEnvironment('a', defaultValue: 42);
+''');
+
+ await assertNoErrorsInCode(r'''
+// @dart = 2.7
+import 'a.dart';
+
+const b = a;
+''');
+
+ var c = findElement.topVar('b');
+ assertType(c.type, 'int*');
+ _assertIntValue(c, 42);
+ }
+
void _assertIntValue(VariableElement element, int value) {
expect(element.constantValue.toIntValue(), value);
}
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index e06795d..2887b49 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -1064,6 +1064,7 @@
} else if (node.parent is Annotation) {
optype.includeConstructorSuggestions = true;
} else {
+ optype.completionLocation = 'PropertyAccess_propertyName';
optype.includeReturnValueSuggestions = true;
optype.includeTypeNameSuggestions = true;
optype.includeVoidReturnSuggestions =
diff --git a/pkg/analyzer_plugin/lib/utilities/assist/assist.dart b/pkg/analyzer_plugin/lib/utilities/assist/assist.dart
index 00dedd3..d6fa2a4 100644
--- a/pkg/analyzer_plugin/lib/utilities/assist/assist.dart
+++ b/pkg/analyzer_plugin/lib/utilities/assist/assist.dart
@@ -65,7 +65,8 @@
///
/// Clients may not extend, implement or mix-in this class.
class AssistKind {
- /// The unique identifier of this kind of assist.
+ /// The unique identifier of this kind of assist. May be used by client editors,
+ /// for example to allow key-binding specific fixes (or groups of).
final String id;
/// The priority of this kind of assist for the kind of error being addressed.
diff --git a/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart b/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart
index ac3c5fe..46c3a7d 100644
--- a/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart
+++ b/pkg/analyzer_plugin/lib/utilities/fixes/fixes.dart
@@ -91,8 +91,9 @@
///
/// Clients may not extend, implement or mix-in this class.
class FixKind {
- /// The name of this kind of fix, used for debugging.
- final String name;
+ /// The unique identifier of this kind of assist. May be used by client editors,
+ /// for example to allow key-binding specific fixes (or groups of).
+ final String id;
/// The priority of this kind of fix for the kind of error being addressed
/// where a higher integer value indicates a higher priority and relevance.
@@ -108,21 +109,21 @@
/// kind of 'applied together' fix.
final String appliedTogetherMessage;
- /// Initialize a newly created kind of fix to have the given [name],
+ /// Initialize a newly created kind of fix to have the given [id],
/// [priority], [message], and optionally [canBeAppliedTogether] and
/// [appliedTogetherMessage].
- const FixKind(this.name, this.priority, this.message,
+ const FixKind(this.id, this.priority, this.message,
{this.appliedTogetherMessage});
@override
- int get hashCode => name.hashCode;
+ int get hashCode => id.hashCode;
@override
- bool operator ==(o) => o is FixKind && o.name == name;
+ bool operator ==(o) => o is FixKind && o.id == id;
/// The change can be made with other fixes of this [FixKind].
bool canBeAppliedTogether() => appliedTogetherMessage != null;
@override
- String toString() => name;
+ String toString() => id;
}
diff --git a/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart b/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart
index 7292c68..1613b9d 100644
--- a/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/completion/optype_test.dart
@@ -271,7 +271,11 @@
// SimpleIdentifier PrefixedIdentifier ArgumentList
addTestSource('void main() {expect(aa.^)}');
await assertOpType(
- constructors: true, returnValue: true, typeNames: true, prefixed: true);
+ completionLocation: 'PropertyAccess_propertyName',
+ constructors: true,
+ prefixed: true,
+ returnValue: true,
+ typeNames: true);
}
Future<void> test_argumentList_resolved() async {
@@ -614,6 +618,7 @@
Future<void> test_block_keyword() async {
addTestSource('class C { static C get instance => null; } main() {C.in^}');
await assertOpType(
+ completionLocation: 'PropertyAccess_propertyName',
constructors: true,
prefixed: true,
returnValue: true,
@@ -1194,33 +1199,33 @@
// FormalParameterList MethodDeclaration
addTestSource('class A {a(b.^ f) { }}');
await assertOpType(
- constructors: true,
- returnValue: true,
- typeNames: true,
- prefixed: true,
- );
+ completionLocation: 'PropertyAccess_propertyName',
+ constructors: true,
+ prefixed: true,
+ returnValue: true,
+ typeNames: true);
}
Future<void> test_formalParameter_partialType2() async {
// FormalParameterList MethodDeclaration
addTestSource('class A {a(b.z^ f) { }}');
await assertOpType(
- constructors: true,
- returnValue: true,
- typeNames: true,
- prefixed: true,
- );
+ completionLocation: 'PropertyAccess_propertyName',
+ constructors: true,
+ prefixed: true,
+ returnValue: true,
+ typeNames: true);
}
Future<void> test_formalParameter_partialType3() async {
// FormalParameterList MethodDeclaration
addTestSource('class A {a(b.^) { }}');
await assertOpType(
- constructors: true,
- returnValue: true,
- typeNames: true,
- prefixed: true,
- );
+ completionLocation: 'PropertyAccess_propertyName',
+ constructors: true,
+ prefixed: true,
+ returnValue: true,
+ typeNames: true);
}
Future<void> test_formalParameterList_empty() async {
@@ -1551,7 +1556,11 @@
// SimpleIdentifier PrefixIdentifier IfStatement
addTestSource('main() {var a; if (a.^) something}');
await assertOpType(
- constructors: true, returnValue: true, typeNames: true, prefixed: true);
+ completionLocation: 'PropertyAccess_propertyName',
+ constructors: true,
+ prefixed: true,
+ returnValue: true,
+ typeNames: true);
}
Future<void> test_implementsClause_beginning() async {
@@ -1654,7 +1663,11 @@
// SimpleIdentifier PrefixedIdentifier InterpolationExpression
addTestSource('main() {String name; print("hello \${name.^}");}');
await assertOpType(
- constructors: true, returnValue: true, typeNames: true, prefixed: true);
+ completionLocation: 'PropertyAccess_propertyName',
+ constructors: true,
+ prefixed: true,
+ returnValue: true,
+ typeNames: true);
}
Future<void> test_interpolationExpression_prefix_target() async {
@@ -2025,22 +2038,24 @@
// SimpleIdentifier PrefixedIdentifier ExpressionStatement Block
addTestSource('main() {A.^}');
await assertOpType(
+ completionLocation: 'PropertyAccess_propertyName',
constructors: true,
+ prefixed: true,
returnValue: true,
typeNames: true,
- voidReturn: true,
- prefixed: true);
+ voidReturn: true);
}
Future<void> test_prefixedIdentifier_class_imported() async {
// SimpleIdentifier PrefixedIdentifier ExpressionStatement
addTestSource('main() {A a; a.^}');
await assertOpType(
+ completionLocation: 'PropertyAccess_propertyName',
constructors: true,
+ prefixed: true,
returnValue: true,
typeNames: true,
- voidReturn: true,
- prefixed: true);
+ voidReturn: true);
}
Future<void> test_prefixedIdentifier_prefix() async {
diff --git a/pkg/async_helper/lib/async_minitest.dart b/pkg/async_helper/lib/async_minitest.dart
index cf60812..57a05eb 100644
--- a/pkg/async_helper/lib/async_minitest.dart
+++ b/pkg/async_helper/lib/async_minitest.dart
@@ -99,7 +99,7 @@
dynamic expectAsync(Function f, {int count = 1}) {
var f2 = f; // Avoid type-promoting f, we want dynamic invocations.
- if (f2 is Function(Null, Null, Null, Null, Null)) {
+ if (f2 is Function(Never, Never, Never, Never, Never)) {
asyncStart(count);
return ([a, b, c, d, e]) {
var result = f(a, b, c, d, e);
@@ -107,7 +107,7 @@
return result;
};
}
- if (f2 is Function(Null, Null, Null, Null)) {
+ if (f2 is Function(Never, Never, Never, Never)) {
asyncStart(count);
return ([a, b, c, d]) {
var result = f(a, b, c, d);
@@ -115,7 +115,7 @@
return result;
};
}
- if (f2 is Function(Null, Null, Null)) {
+ if (f2 is Function(Never, Never, Never)) {
asyncStart(count);
return ([a, b, c]) {
var result = f(a, b, c);
@@ -123,7 +123,7 @@
return result;
};
}
- if (f2 is Function(Null, Null)) {
+ if (f2 is Function(Never, Never)) {
asyncStart(count);
return ([a, b]) {
var result = f(a, b);
@@ -131,7 +131,7 @@
return result;
};
}
- if (f2 is Function(Null)) {
+ if (f2 is Function(Never)) {
asyncStart(count);
return ([a]) {
var result = f(a);
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 646cdee..bc001f7 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -276,8 +276,10 @@
});
int parameterIndex = 0;
closedWorld.elementEnvironment.forEachParameter(function, (type, name, _) {
- parameters.add(new ParameterInfo(
- name, inferredParameterTypes[parameterIndex++], '$type'));
+ // Synthesized parameters have no name. This can happen on parameters of
+ // setters derived from lowering late fields.
+ parameters.add(new ParameterInfo(name ?? '#t${parameterIndex}',
+ inferredParameterTypes[parameterIndex++], '$type'));
});
var functionType = environment.getFunctionType(function);
diff --git a/pkg/compiler/lib/src/js_backend/specialized_checks.dart b/pkg/compiler/lib/src/js_backend/specialized_checks.dart
index 9df9562..8651bf2 100644
--- a/pkg/compiler/lib/src/js_backend/specialized_checks.dart
+++ b/pkg/compiler/lib/src/js_backend/specialized_checks.dart
@@ -24,6 +24,20 @@
class SpecializedChecks {
static IsTestSpecialization findIsTestSpecialization(
DartType dartType, HGraph graph, JClosedWorld closedWorld) {
+ if (dartType is LegacyType) {
+ DartType base = dartType.baseType;
+ // `Never*` accepts only `null`.
+ if (base is NeverType) return IsTestSpecialization.null_;
+ // TODO(sra): Handle strong checking 'x is Object' --> `x != null`.
+ // `Object*` is top and should be handled by constant folding.
+ if (base.isObject) return null;
+ return _findIsTestSpecialization(base, graph, closedWorld);
+ }
+ return _findIsTestSpecialization(dartType, graph, closedWorld);
+ }
+
+ static IsTestSpecialization _findIsTestSpecialization(
+ DartType dartType, HGraph graph, JClosedWorld closedWorld) {
if (dartType is InterfaceType) {
ClassEntity element = dartType.element;
JCommonElements commonElements = closedWorld.commonElements;
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index b5848269..781976a 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -3518,7 +3518,13 @@
break;
case IsTestSpecialization.instanceof:
- InterfaceType type = node.dartType;
+ DartType dartType = node.dartType;
+ // We don't generate instancof specializations for Never* and Object*.
+ assert(dartType is InterfaceType ||
+ (dartType is LegacyType &&
+ !dartType.baseType.isObject &&
+ dartType.baseType is! NeverType));
+ InterfaceType type = dartType.withoutNullability;
_registry.registerTypeUse(TypeUse.instanceConstructor(type));
test = handleNegative(js.js('# instanceof #',
[value, _emitter.constructorAccess(type.element)]));
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 695aca3..414e7a3 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -4636,8 +4636,19 @@
// is definitely false, so we reuse some of the case-by-case logic from the
// old [HIs] optimization.
if (closedWorld.dartTypes.isTopType(dartType)) return AbstractBool.True;
- if (dartType is! InterfaceType) return AbstractBool.Maybe;
- InterfaceType type = dartType;
+
+ InterfaceType type;
+ if (dartType is InterfaceType) {
+ type = dartType;
+ } else if (dartType is LegacyType) {
+ DartType base = dartType.baseType;
+ if (base is! InterfaceType) return AbstractBool.Maybe;
+ assert(!base.isObject); // Top type handled above;
+ type = base;
+ } else {
+ return AbstractBool.Maybe;
+ }
+
ClassEntity element = type.element;
if (type.typeArguments.isNotEmpty) return AbstractBool.Maybe;
JCommonElements commonElements = closedWorld.commonElements;
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 29106f1..6571a7a 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -58,6 +58,9 @@
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: false)
+ ..addFlag('tree-shake-write-only-fields',
+ help: 'Enable tree shaking of fields which are only written in AOT mode.',
+ defaultsTo: false)
..addFlag('protobuf-tree-shaker',
help: 'Enable protobuf tree shaker transformation in AOT mode.',
defaultsTo: false)
@@ -520,7 +523,8 @@
environmentDefines: environmentDefines,
enableAsserts: options['enable-asserts'],
useProtobufTreeShaker: options['protobuf-tree-shaker'],
- minimalKernel: options['minimal-kernel']));
+ minimalKernel: options['minimal-kernel'],
+ treeShakeWriteOnlyFields: options['tree-shake-write-only-fields']));
}
if (results.component != null) {
transformer?.transform(results.component);
@@ -909,46 +913,45 @@
Map<String, String> jsFrameValues,
String moduleName,
String expression) async {
- final String boundaryKey = Uuid().generateV4();
- _outputStream.writeln('result $boundaryKey');
-
_generator.accept();
errors.clear();
- if (_bundler != null) {
- var kernel2jsCompiler = _bundler.compilers[moduleName];
- if (kernel2jsCompiler == null) {
- throw Exception('Cannot find kernel2js compiler for $moduleName. '
- 'Compilers are avaiable for modules: '
- '\n\t${_bundler.compilers.keys.toString()}');
- }
- assert(kernel2jsCompiler != null);
+ if (_bundler == null) {
+ reportError('JavaScript bundler is null');
+ return;
+ }
+ if (!_bundler.compilers.containsKey(moduleName)) {
+ reportError('Cannot find kernel2js compiler for $moduleName.');
+ return;
+ }
- var evaluator = new ExpressionCompiler(
- _generator.generator, kernel2jsCompiler, _component,
- verbose: _compilerOptions.verbose,
- onDiagnostic: _compilerOptions.onDiagnostic);
+ final String boundaryKey = Uuid().generateV4();
+ _outputStream.writeln('result $boundaryKey');
- var procedure = await evaluator.compileExpressionToJs(libraryUri, line,
- column, jsModules, jsFrameValues, moduleName, expression);
+ var kernel2jsCompiler = _bundler.compilers[moduleName];
- var result = errors.length > 0 ? errors[0] : procedure;
+ var evaluator = new ExpressionCompiler(
+ _generator.generator, kernel2jsCompiler, _component,
+ verbose: _compilerOptions.verbose,
+ onDiagnostic: _compilerOptions.onDiagnostic);
- // TODO(annagrin): kernelBinaryFilename is too specific
- // rename to _outputFileName?
- await File(_kernelBinaryFilename).writeAsString(result);
+ var procedure = await evaluator.compileExpressionToJs(libraryUri, line,
+ column, jsModules, jsFrameValues, moduleName, expression);
- _outputStream
- .writeln('$boundaryKey $_kernelBinaryFilename ${errors.length}');
+ var result = errors.length > 0 ? errors[0] : procedure;
- // TODO(annagrin): do we need to add asserts/error reporting if
- // initial compilation didn't happen and _kernelBinaryFilename
- // is different from below?
- if (procedure != null) {
- _kernelBinaryFilename = _kernelBinaryFilenameIncremental;
- }
- } else {
- _outputStream.writeln('$boundaryKey');
+ // TODO(annagrin): kernelBinaryFilename is too specific
+ // rename to _outputFileName?
+ await File(_kernelBinaryFilename).writeAsString(result);
+
+ _outputStream
+ .writeln('$boundaryKey $_kernelBinaryFilename ${errors.length}');
+
+ // TODO(annagrin): do we need to add asserts/error reporting if
+ // initial compilation didn't happen and _kernelBinaryFilename
+ // is different from below?
+ if (procedure != null) {
+ _kernelBinaryFilename = _kernelBinaryFilenameIncremental;
}
}
diff --git a/pkg/kernel/lib/text/serializer_combinators.dart b/pkg/kernel/lib/text/serializer_combinators.dart
index e166cb2..45de839 100644
--- a/pkg/kernel/lib/text/serializer_combinators.dart
+++ b/pkg/kernel/lib/text/serializer_combinators.dart
@@ -185,6 +185,19 @@
}
}
+class UriSerializer extends TextSerializer<Uri> {
+ const UriSerializer();
+
+ Uri readFrom(Iterator<Object> stream, DeserializationState state) {
+ String uriAsString = const DartString().readFrom(stream, state);
+ return Uri.parse(uriAsString);
+ }
+
+ void writeTo(StringBuffer buffer, Uri object, SerializationState state) {
+ const DartString().writeTo(buffer, object.toString(), state);
+ }
+}
+
// == Serializers for tagged (disjoint) unions.
//
// They require a function mapping serializables to a tag string. This is
@@ -195,7 +208,7 @@
final List<String> tags;
final List<TextSerializer<T>> serializers;
- Case(this.tagger, this.tags, this.serializers);
+ const Case(this.tagger, this.tags, this.serializers);
Case.uninitialized(this.tagger)
: tags = [],
@@ -249,7 +262,7 @@
final K Function(S) wrap;
final TextSerializer<S> contents;
- Wrapped(this.unwrap, this.wrap, this.contents);
+ const Wrapped(this.unwrap, this.wrap, this.contents);
K readFrom(Iterator<Object> stream, DeserializationState state) {
return wrap(contents.readFrom(stream, state));
diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart
index 467af4d..633e8c6 100644
--- a/pkg/kernel/lib/text/text_serializer.dart
+++ b/pkg/kernel/lib/text/text_serializer.dart
@@ -20,14 +20,14 @@
String tag(Name name) => name.isPrivate ? "private" : "public";
}
-TextSerializer<Name> publicName =
- new Wrapped(unwrapPublicName, wrapPublicName, const DartString());
+const TextSerializer<Name> publicName =
+ const Wrapped(unwrapPublicName, wrapPublicName, const DartString());
String unwrapPublicName(Name name) => name.name;
Name wrapPublicName(String name) => new Name(name);
-TextSerializer<Name> privateName = new Wrapped(unwrapPrivateName,
+const TextSerializer<Name> privateName = const Wrapped(unwrapPrivateName,
wrapPrivateName, Tuple2Serializer(const DartString(), const DartString()));
Tuple2<String, String> unwrapPrivateName(Name name) {
@@ -40,7 +40,7 @@
throw UnimplementedError('deserialization of private names');
}
-TextSerializer<Name> nameSerializer = new Case(const NameTagger(), [
+const TextSerializer<Name> nameSerializer = const Case(const NameTagger(), [
"public",
"private",
], [
@@ -119,8 +119,9 @@
String visitFunctionExpression(FunctionExpression _) => "fun";
}
-TextSerializer<InvalidExpression> invalidExpressionSerializer = new Wrapped(
- unwrapInvalidExpression, wrapInvalidExpression, const DartString());
+const TextSerializer<InvalidExpression> invalidExpressionSerializer =
+ const Wrapped(
+ unwrapInvalidExpression, wrapInvalidExpression, const DartString());
String unwrapInvalidExpression(InvalidExpression expression) {
return expression.message;
@@ -173,57 +174,57 @@
return new StringConcatenation(expressions);
}
-TextSerializer<StringLiteral> stringLiteralSerializer =
- new Wrapped(unwrapStringLiteral, wrapStringLiteral, const DartString());
+const TextSerializer<StringLiteral> stringLiteralSerializer =
+ const Wrapped(unwrapStringLiteral, wrapStringLiteral, const DartString());
String unwrapStringLiteral(StringLiteral literal) => literal.value;
StringLiteral wrapStringLiteral(String value) => new StringLiteral(value);
-TextSerializer<IntLiteral> intLiteralSerializer =
- new Wrapped(unwrapIntLiteral, wrapIntLiteral, const DartInt());
+const TextSerializer<IntLiteral> intLiteralSerializer =
+ const Wrapped(unwrapIntLiteral, wrapIntLiteral, const DartInt());
int unwrapIntLiteral(IntLiteral literal) => literal.value;
IntLiteral wrapIntLiteral(int value) => new IntLiteral(value);
-TextSerializer<DoubleLiteral> doubleLiteralSerializer =
- new Wrapped(unwrapDoubleLiteral, wrapDoubleLiteral, const DartDouble());
+const TextSerializer<DoubleLiteral> doubleLiteralSerializer =
+ const Wrapped(unwrapDoubleLiteral, wrapDoubleLiteral, const DartDouble());
double unwrapDoubleLiteral(DoubleLiteral literal) => literal.value;
DoubleLiteral wrapDoubleLiteral(double value) => new DoubleLiteral(value);
-TextSerializer<BoolLiteral> boolLiteralSerializer =
- new Wrapped(unwrapBoolLiteral, wrapBoolLiteral, const DartBool());
+const TextSerializer<BoolLiteral> boolLiteralSerializer =
+ const Wrapped(unwrapBoolLiteral, wrapBoolLiteral, const DartBool());
bool unwrapBoolLiteral(BoolLiteral literal) => literal.value;
BoolLiteral wrapBoolLiteral(bool value) => new BoolLiteral(value);
-TextSerializer<NullLiteral> nullLiteralSerializer =
- new Wrapped(unwrapNullLiteral, wrapNullLiteral, const Nothing());
+const TextSerializer<NullLiteral> nullLiteralSerializer =
+ const Wrapped(unwrapNullLiteral, wrapNullLiteral, const Nothing());
void unwrapNullLiteral(NullLiteral literal) {}
NullLiteral wrapNullLiteral(void ignored) => new NullLiteral();
-TextSerializer<SymbolLiteral> symbolLiteralSerializer =
- new Wrapped(unwrapSymbolLiteral, wrapSymbolLiteral, const DartString());
+const TextSerializer<SymbolLiteral> symbolLiteralSerializer =
+ const Wrapped(unwrapSymbolLiteral, wrapSymbolLiteral, const DartString());
String unwrapSymbolLiteral(SymbolLiteral expression) => expression.value;
SymbolLiteral wrapSymbolLiteral(String value) => new SymbolLiteral(value);
-TextSerializer<ThisExpression> thisExpressionSerializer =
- new Wrapped(unwrapThisExpression, wrapThisExpression, const Nothing());
+const TextSerializer<ThisExpression> thisExpressionSerializer =
+ const Wrapped(unwrapThisExpression, wrapThisExpression, const Nothing());
void unwrapThisExpression(ThisExpression expression) {}
ThisExpression wrapThisExpression(void ignored) => new ThisExpression();
-TextSerializer<Rethrow> rethrowSerializer =
- new Wrapped(unwrapRethrow, wrapRethrow, const Nothing());
+const TextSerializer<Rethrow> rethrowSerializer =
+ const Wrapped(unwrapRethrow, wrapRethrow, const Nothing());
void unwrapRethrow(Rethrow expression) {}
@@ -439,8 +440,8 @@
return new PropertySet(tuple.first, tuple.second, tuple.third);
}
-TextSerializer<SuperPropertyGet> superPropertyGetSerializer =
- new Wrapped(unwrapSuperPropertyGet, wrapSuperPropertyGet, nameSerializer);
+const TextSerializer<SuperPropertyGet> superPropertyGetSerializer =
+ const Wrapped(unwrapSuperPropertyGet, wrapSuperPropertyGet, nameSerializer);
Name unwrapSuperPropertyGet(SuperPropertyGet expression) {
return expression.name;
@@ -553,8 +554,8 @@
}
}
-TextSerializer<StaticGet> staticGetSerializer =
- new Wrapped(unwrapStaticGet, wrapStaticGet, new CanonicalNameSerializer());
+const TextSerializer<StaticGet> staticGetSerializer = const Wrapped(
+ unwrapStaticGet, wrapStaticGet, const CanonicalNameSerializer());
CanonicalName unwrapStaticGet(StaticGet expression) {
return expression.targetReference.canonicalName;
@@ -831,8 +832,8 @@
constDeclarationSerializer,
]);
-TextSerializer<TypeParameter> typeParameterSerializer =
- new Wrapped(unwrapTypeParameter, wrapTypeParameter, const DartString());
+const TextSerializer<TypeParameter> typeParameterSerializer =
+ const Wrapped(unwrapTypeParameter, wrapTypeParameter, const DartString());
String unwrapTypeParameter(TypeParameter node) => node.name;
@@ -889,29 +890,29 @@
String visitTypeParameterType(TypeParameterType _) => "par";
}
-TextSerializer<InvalidType> invalidTypeSerializer =
- new Wrapped(unwrapInvalidType, wrapInvalidType, const Nothing());
+const TextSerializer<InvalidType> invalidTypeSerializer =
+ const Wrapped(unwrapInvalidType, wrapInvalidType, const Nothing());
void unwrapInvalidType(InvalidType type) {}
InvalidType wrapInvalidType(void ignored) => const InvalidType();
-TextSerializer<DynamicType> dynamicTypeSerializer =
- new Wrapped(unwrapDynamicType, wrapDynamicType, const Nothing());
+const TextSerializer<DynamicType> dynamicTypeSerializer =
+ const Wrapped(unwrapDynamicType, wrapDynamicType, const Nothing());
void unwrapDynamicType(DynamicType type) {}
DynamicType wrapDynamicType(void ignored) => const DynamicType();
-TextSerializer<VoidType> voidTypeSerializer =
- new Wrapped(unwrapVoidType, wrapVoidType, const Nothing());
+const TextSerializer<VoidType> voidTypeSerializer =
+ const Wrapped(unwrapVoidType, wrapVoidType, const Nothing());
void unwrapVoidType(VoidType type) {}
VoidType wrapVoidType(void ignored) => const VoidType();
-TextSerializer<BottomType> bottomTypeSerializer =
- new Wrapped(unwrapBottomType, wrapBottomType, const Nothing());
+const TextSerializer<BottomType> bottomTypeSerializer =
+ const Wrapped(unwrapBottomType, wrapBottomType, const Nothing());
void unwrapBottomType(BottomType type) {}
@@ -1258,6 +1259,31 @@
Case<Procedure> procedureSerializer =
new Case.uninitialized(const ProcedureTagger());
+class LibraryTagger implements Tagger<Library> {
+ const LibraryTagger();
+
+ String tag(Library node) {
+ return node.isNonNullableByDefault ? "null-safe" : "legacy";
+ }
+}
+
+TextSerializer<Library> libraryContentsSerializer = new Wrapped(
+ unwrapLibraryNode,
+ wrapLibraryNode,
+ new Tuple2Serializer(
+ const UriSerializer(), new ListSerializer(procedureSerializer)),
+);
+
+Tuple2<Uri, List<Procedure>> unwrapLibraryNode(Library library) {
+ return new Tuple2(library.importUri, library.procedures);
+}
+
+Library wrapLibraryNode(Tuple2<Uri, List<Procedure>> tuple) {
+ return new Library(tuple.first, procedures: tuple.second);
+}
+
+Case<Library> librarySerializer = new Case.uninitialized(const LibraryTagger());
+
void initializeSerializers() {
expressionSerializer.tags.addAll([
"string",
@@ -1391,4 +1417,9 @@
]);
procedureSerializer.tags.addAll(["static-method"]);
procedureSerializer.serializers.addAll([staticMethodSerializer]);
+ librarySerializer.tags.addAll(["legacy", "null-safe"]);
+ librarySerializer.serializers.addAll([
+ libraryContentsSerializer,
+ libraryContentsSerializer,
+ ]);
}
diff --git a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
index 2857826..eb2f032 100644
--- a/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
+++ b/pkg/kernel/test/text_serializer_from_kernel_nodes_test.dart
@@ -13,87 +13,42 @@
test();
}
-abstract class TestCase<T extends Node> {
+class TestCase<T extends Node> {
final String name;
final T node;
final SerializationState serializationState;
final DeserializationState deserializationState;
final String expectation;
+ final TextSerializer<T> serializer;
- TestCase._(
+ TestCase(
{this.name,
this.node,
this.expectation,
+ this.serializer,
SerializationState serializationState,
DeserializationState deserializationState})
- : this.serializationState =
+ : assert(node != null),
+ assert(expectation != null),
+ assert(serializer != null),
+ this.serializationState =
serializationState ?? new SerializationState(null),
this.deserializationState = deserializationState ??
new DeserializationState(null, new CanonicalName.root());
- T readNode(String input, DeserializationState state);
-
- String writeNode(T node, SerializationState state);
-}
-
-class StatementTestCase extends TestCase<Statement> {
- StatementTestCase(
- {String name,
- Statement node,
- String expectation,
- SerializationState serializationState,
- DeserializationState deserializationState})
- : super._(
- name: name,
- node: node,
- expectation: expectation,
- serializationState: serializationState,
- deserializationState: deserializationState);
-
- Statement readNode(String input, DeserializationState state) {
+ T readNode(String input, DeserializationState state) {
TextIterator stream = new TextIterator(input, 0);
stream.moveNext();
- Statement result = statementSerializer.readFrom(stream, state);
+ T result = serializer.readFrom(stream, state);
if (stream.moveNext()) {
- throw new StateError("Found extra tokens at the end of the statement.");
+ throw new StateError("Found extra tokens at the end.");
}
return result;
}
- String writeNode(Statement statement, SerializationState state) {
+ String writeNode(T node, SerializationState state) {
StringBuffer buffer = new StringBuffer();
- statementSerializer.writeTo(buffer, statement, state);
- return buffer.toString();
- }
-}
-
-class ProcedureTestCase extends TestCase<Procedure> {
- ProcedureTestCase(
- {String name,
- Procedure node,
- String expectation,
- SerializationState serializationState,
- DeserializationState deserializationState})
- : super._(
- name: name,
- node: node,
- expectation: expectation,
- serializationState: serializationState,
- deserializationState: deserializationState);
-
- Procedure readNode(String input, DeserializationState state) {
- TextIterator stream = new TextIterator(input, 0);
- stream.moveNext();
- Procedure result = procedureSerializer.readFrom(stream, state);
- if (stream.moveNext()) {
- throw new StateError("Found extra tokens at the end of the procedure.");
- }
- return result;
- }
-
- String writeNode(Procedure procedure, SerializationState state) {
- StringBuffer buffer = new StringBuffer();
- procedureSerializer.writeTo(buffer, procedure, state);
+ serializer.writeTo(buffer, node, state);
return buffer.toString();
}
}
@@ -101,7 +56,7 @@
void test() {
List<String> failures = [];
List<TestCase> tests = <TestCase>[
- new StatementTestCase(
+ new TestCase<Statement>(
name: 'let dynamic x = 42 in x;',
node: () {
VariableDeclaration x = new VariableDeclaration('x',
@@ -110,8 +65,9 @@
}(),
expectation: ''
'(expr (let (var "x^0" (dynamic) (int 42) ())'
- ' (get-var "x^0" _)))'),
- new StatementTestCase(
+ ' (get-var "x^0" _)))',
+ serializer: statementSerializer),
+ new TestCase<Statement>(
name: 'let dynamic x = 42 in let Bottom x^0 = null in x;',
node: () {
VariableDeclaration outterLetVar = new VariableDeclaration('x',
@@ -124,8 +80,9 @@
expectation: ''
'(expr (let (var "x^0" (dynamic) (int 42) ())'
' (let (var "x^1" (bottom) (null) ())'
- ' (get-var "x^0" _))))'),
- new StatementTestCase(
+ ' (get-var "x^0" _))))',
+ serializer: statementSerializer),
+ new TestCase<Statement>(
name: 'let dynamic x = 42 in let Bottom x^0 = null in x^0;',
node: () {
VariableDeclaration outterLetVar = new VariableDeclaration('x',
@@ -138,11 +95,12 @@
expectation: ''
'(expr (let (var "x^0" (dynamic) (int 42) ())'
' (let (var "x^1" (bottom) (null) ())'
- ' (get-var "x^1" _))))'),
+ ' (get-var "x^1" _))))',
+ serializer: statementSerializer),
() {
VariableDeclaration x =
new VariableDeclaration('x', type: const DynamicType());
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose: dynamic x; */ x = 42;',
node: new ExpressionStatement(new VariableSet(x, new IntLiteral(42))),
expectation: '(expr (set-var "x^0" (int 42)))',
@@ -155,7 +113,8 @@
new DeserializationEnvironment(null)
..addBinder('x^0', x)
..close(),
- new CanonicalName.root()));
+ new CanonicalName.root()),
+ serializer: statementSerializer);
}(),
() {
Field field = new Field(new Name('field'), type: const DynamicType());
@@ -164,13 +123,14 @@
fields: <Field>[field]);
Component component = new Component(libraries: <Library>[library]);
component.computeCanonicalNames();
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose top-level: dynamic field; */ field;',
node: new ExpressionStatement(new StaticGet(field)),
expectation: ''
'(expr (get-static "package:foo/bar.dart::@fields::field"))',
serializationState: new SerializationState(null),
- deserializationState: new DeserializationState(null, component.root));
+ deserializationState: new DeserializationState(null, component.root),
+ serializer: statementSerializer);
}(),
() {
Field field = new Field(new Name('field'), type: const DynamicType());
@@ -179,7 +139,7 @@
fields: <Field>[field]);
Component component = new Component(libraries: <Library>[library]);
component.computeCanonicalNames();
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose top-level: dynamic field; */ field = 1;',
node:
new ExpressionStatement(new StaticSet(field, new IntLiteral(1))),
@@ -187,7 +147,8 @@
'(expr'
' (set-static "package:foo/bar.dart::@fields::field" (int 1)))',
serializationState: new SerializationState(null),
- deserializationState: new DeserializationState(null, component.root));
+ deserializationState: new DeserializationState(null, component.root),
+ serializer: statementSerializer);
}(),
() {
Procedure topLevelProcedure = new Procedure(
@@ -202,7 +163,7 @@
procedures: <Procedure>[topLevelProcedure]);
Component component = new Component(libraries: <Library>[library]);
component.computeCanonicalNames();
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose top-level: foo(dynamic x) {...}; */ foo(42);',
node: new ExpressionStatement(new StaticInvocation.byReference(
topLevelProcedure.reference,
@@ -212,7 +173,8 @@
'(expr (invoke-static "package:foo/bar.dart::@methods::foo"'
' () ((int 42)) ()))',
serializationState: new SerializationState(null),
- deserializationState: new DeserializationState(null, component.root));
+ deserializationState: new DeserializationState(null, component.root),
+ serializer: statementSerializer);
}(),
() {
Procedure factoryConstructor = new Procedure(
@@ -225,7 +187,7 @@
classes: <Class>[klass]);
Component component = new Component(libraries: <Library>[library]);
component.computeCanonicalNames();
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: ''
'/* suppose A { const A(); const factory A.foo() = A; } */'
' const A.foo();',
@@ -237,7 +199,8 @@
' "package:foo/bar.dart::A::@factories::foo"'
' () () ()))',
serializationState: new SerializationState(null),
- deserializationState: new DeserializationState(null, component.root));
+ deserializationState: new DeserializationState(null, component.root),
+ serializer: statementSerializer);
}(),
() {
Field field = new Field(new Name('field'), type: const DynamicType());
@@ -250,7 +213,7 @@
VariableDeclaration x =
new VariableDeclaration('x', type: const DynamicType());
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose A {dynamic field;} A x; */ x.{A::field};',
node: new ExpressionStatement(new DirectPropertyGet.byReference(
new VariableGet(x), field.reference)),
@@ -265,7 +228,8 @@
new DeserializationEnvironment(null)
..addBinder('x^0', x)
..close(),
- component.root));
+ component.root),
+ serializer: statementSerializer);
}(),
() {
Field field = new Field(new Name('field'), type: const DynamicType());
@@ -278,7 +242,7 @@
VariableDeclaration x =
new VariableDeclaration('x', type: const DynamicType());
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose A {dynamic field;} A x; */ x.{A::field} = 42;',
node: new ExpressionStatement(new DirectPropertySet.byReference(
new VariableGet(x), field.reference, new IntLiteral(42))),
@@ -293,7 +257,8 @@
new DeserializationEnvironment(null)
..addBinder('x^0', x)
..close(),
- component.root));
+ component.root),
+ serializer: statementSerializer);
}(),
() {
Procedure method = new Procedure(
@@ -308,7 +273,7 @@
VariableDeclaration x =
new VariableDeclaration('x', type: const DynamicType());
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose A {foo() {...}} A x; */ x.{A::foo}();',
node: new ExpressionStatement(new DirectMethodInvocation.byReference(
new VariableGet(x), method.reference, new Arguments([]))),
@@ -324,7 +289,8 @@
new DeserializationEnvironment(null)
..addBinder('x^0', x)
..close(),
- component.root));
+ component.root),
+ serializer: statementSerializer);
}(),
() {
Constructor constructor =
@@ -336,7 +302,7 @@
classes: <Class>[klass]);
Component component = new Component(libraries: <Library>[library]);
component.computeCanonicalNames();
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose A {A.foo();} */ new A();',
node: new ExpressionStatement(new ConstructorInvocation.byReference(
constructor.reference, new Arguments([]))),
@@ -345,7 +311,8 @@
' "package:foo/bar.dart::A::@constructors::foo"'
' () () ()))',
serializationState: new SerializationState(null),
- deserializationState: new DeserializationState(null, component.root));
+ deserializationState: new DeserializationState(null, component.root),
+ serializer: statementSerializer);
}(),
() {
Constructor constructor = new Constructor(new FunctionNode(null),
@@ -357,7 +324,7 @@
classes: <Class>[klass]);
Component component = new Component(libraries: <Library>[library]);
component.computeCanonicalNames();
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* suppose A {const A.foo();} */ const A();',
node: new ExpressionStatement(new ConstructorInvocation.byReference(
constructor.reference, new Arguments([]),
@@ -367,14 +334,15 @@
' "package:foo/bar.dart::A::@constructors::foo"'
' () () ()))',
serializationState: new SerializationState(null),
- deserializationState: new DeserializationState(null, component.root));
+ deserializationState: new DeserializationState(null, component.root),
+ serializer: statementSerializer);
}(),
() {
TypeParameter outterParam =
new TypeParameter('T', const DynamicType(), const DynamicType());
TypeParameter innerParam =
new TypeParameter('T', const DynamicType(), const DynamicType());
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* T Function<T>(T Function<T>()); */',
node: new ExpressionStatement(new TypeLiteral(new FunctionType(
[
@@ -394,7 +362,8 @@
serializationState:
new SerializationState(new SerializationEnvironment(null)),
deserializationState: new DeserializationState(
- new DeserializationEnvironment(null), null));
+ new DeserializationEnvironment(null), null),
+ serializer: statementSerializer);
}(),
() {
TypeParameter t =
@@ -403,7 +372,7 @@
type: new TypeParameterType(t, Nullability.legacy));
VariableDeclaration t2 = new VariableDeclaration('t2',
type: new TypeParameterType(t, Nullability.legacy));
- return new StatementTestCase(
+ return new TestCase<Statement>(
name: '/* <T>(T t1, [T t2]) => t1; */',
node: new ExpressionStatement(new FunctionExpression(new FunctionNode(
new ReturnStatement(new VariableGet(t1)),
@@ -420,7 +389,8 @@
serializationState:
new SerializationState(new SerializationEnvironment(null)),
deserializationState: new DeserializationState(
- new DeserializationEnvironment(null), null));
+ new DeserializationEnvironment(null), null),
+ serializer: statementSerializer);
}(),
() {
VariableDeclaration x = VariableDeclaration('x', type: DynamicType());
@@ -434,16 +404,59 @@
procedures: [foo]);
Component component = Component(libraries: [library]);
component.computeCanonicalNames();
- return new ProcedureTestCase(
+ return new TestCase<Procedure>(
name: 'foo(x) => x;',
node: foo,
expectation: ''
'(static-method (public "foo")'
- ' (sync () () () ((var "x^0" (dynamic) _ ())) () () (dynamic) (ret (get-var "x^0" _))))',
+ ' (sync () () () ((var "x^0" (dynamic) _ ())) () ()'
+ ' (dynamic) (ret (get-var "x^0" _))))',
serializationState:
new SerializationState(new SerializationEnvironment(null)),
deserializationState: new DeserializationState(
- new DeserializationEnvironment(null), null));
+ new DeserializationEnvironment(null), null),
+ serializer: procedureSerializer);
+ }(),
+ () {
+ VariableDeclaration x1 = VariableDeclaration('x', type: DynamicType());
+ VariableDeclaration x2 = VariableDeclaration('x', type: DynamicType());
+ Procedure foo = Procedure(
+ Name('foo'),
+ ProcedureKind.Method,
+ FunctionNode(ReturnStatement(VariableGet(x1)),
+ positionalParameters: [x1]),
+ isStatic: true);
+ Procedure bar = Procedure(
+ Name('bar'),
+ ProcedureKind.Method,
+ FunctionNode(
+ ReturnStatement(
+ StaticInvocation(foo, Arguments([VariableGet(x2)]))),
+ positionalParameters: [x2]),
+ isStatic: true);
+ Library library = Library(Uri(scheme: 'package', path: 'foo/bar.dart'),
+ procedures: [foo, bar]);
+ Component component = Component(libraries: [library]);
+ component.computeCanonicalNames();
+ return new TestCase<Library>(
+ name: 'foo(x) => x; bar(x) => foo(x);',
+ node: library,
+ expectation: ''
+ '(legacy "package:foo/bar.dart"'
+ ''
+ ' ((static-method (public "foo")'
+ ' (sync () () () ((var "x^0" (dynamic) _ ())) () () (dynamic)'
+ ' (ret (get-var "x^0" _))))'
+ ''
+ ' (static-method (public "bar")'
+ ' (sync () () () ((var "x^0" (dynamic) _ ())) () () (dynamic)'
+ ' (ret (invoke-static "package:foo/bar.dart::@methods::foo"'
+ ' () ((get-var "x^0" _)) ()))))))',
+ serializationState:
+ new SerializationState(new SerializationEnvironment(null)),
+ deserializationState: new DeserializationState(
+ new DeserializationEnvironment(null), new CanonicalName.root()),
+ serializer: librarySerializer);
}(),
];
for (TestCase testCase in tests) {
@@ -451,8 +464,8 @@
testCase.writeNode(testCase.node, testCase.serializationState);
if (roundTripInput != testCase.expectation) {
failures.add(''
- '* initial serialization for test "${testCase.name}"'
- ' gave output "${roundTripInput}"');
+ "* initial serialization for test '${testCase.name}'"
+ " gave output '${roundTripInput}'");
}
TreeNode deserialized =
@@ -461,7 +474,7 @@
testCase.writeNode(deserialized, testCase.serializationState);
if (roundTripOutput != roundTripInput) {
failures.add(''
- '* input "${testCase.name}" gave output "${roundTripOutput}"');
+ "* input '${testCase.name}' gave output '${roundTripOutput}'");
}
}
if (failures.isNotEmpty) {
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index d32b605..c1c0d0f 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -13,6 +13,8 @@
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/src/nullability_migration_impl.dart';
+export 'package:nnbd_migration/src/utilities/hint_utils.dart' show HintComment;
+
/// Description of fixes that might be performed by nullability migration.
class NullabilityFixDescription {
/// An if-test or conditional expression needs to have its condition and
@@ -24,6 +26,12 @@
kind: NullabilityFixKind.removeDeadCode,
);
+ /// A variable declaration needs to be marked as "late" due to the presence of
+ /// a `/*late*/` hint.
+ static const addLateDueToHint = const NullabilityFixDescription._(
+ appliedMessage: 'Added a late keyword, due to a hint',
+ kind: NullabilityFixKind.addLateDueToHint);
+
/// An if-test or conditional expression needs to have its condition
/// discarded.
static const discardCondition = const NullabilityFixDescription._(
@@ -172,6 +180,7 @@
/// An enumeration of the various kinds of nullability fixes.
enum NullabilityFixKind {
+ addLateDueToHint,
addRequired,
checkExpression,
checkExpressionDueToHint,
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 6111ba3..8b82454 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -208,7 +208,7 @@
final Set<PromotableElement> _lateHintedLocals = {};
- Map<Token, NullabilityComment> _nullCheckHints = {};
+ Map<Token, HintComment> _nullCheckHints = {};
EdgeBuilder(this.typeProvider, this._typeSystem, this._variables, this._graph,
this.source, this.listener, this._decoratedClassHierarchy,
@@ -510,7 +510,7 @@
_fieldsNotInitializedAtDeclaration = {
for (var member in members)
if (member is FieldDeclaration &&
- !_variables.isLateHinted(source, member.fields))
+ _variables.getLateHint(source, member.fields) == null)
for (var field in member.fields.variables)
if (!field.declaredElement.isStatic && field.initializer == null)
field.declaredElement as FieldElement
@@ -1650,7 +1650,7 @@
} else {
assert(_flowAnalysis != null);
if (declaredElement is PromotableElement &&
- _variables.isLateHinted(source, node)) {
+ _variables.getLateHint(source, node) != null) {
_lateHintedLocals.add(declaredElement);
}
}
@@ -1688,7 +1688,7 @@
// when processing variable reads (only if flow analysis indicates
// the variable isn't definitely assigned).
if (isTopLevel &&
- !_variables.isLateHinted(source, node) &&
+ _variables.getLateHint(source, node) == null &&
!(declaredElement is FieldElement && !declaredElement.isStatic)) {
_graph.makeNullable(
type.node, ImplicitNullInitializerOrigin(source, node));
@@ -2529,12 +2529,12 @@
// Already visited this location.
return type;
}
- switch (_nullCheckHints[token] = getPostfixHint(token)) {
- case NullabilityComment.bang:
- _variables.recordNullCheckHint(source, expression);
- return type.withNode(_graph.never);
- default:
- return type;
+ var hint = _nullCheckHints[token] = getPostfixHint(token);
+ if (hint != null && hint.kind == HintCommentKind.bang) {
+ _variables.recordNullCheckHint(source, expression, hint);
+ return type.withNode(_graph.never);
+ } else {
+ return type;
}
}
diff --git a/pkg/nnbd_migration/lib/src/edit_plan.dart b/pkg/nnbd_migration/lib/src/edit_plan.dart
index 300dd76..25ccfef 100644
--- a/pkg/nnbd_migration/lib/src/edit_plan.dart
+++ b/pkg/nnbd_migration/lib/src/edit_plan.dart
@@ -14,6 +14,7 @@
import 'package:nnbd_migration/fix_reason_target.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
+import 'package:nnbd_migration/src/utilities/hint_utils.dart';
Map<int, List<AtomicEdit>> _removeCode(
int offset, int end, _RemovalStyle removalStyle, AtomicEditInfo info) {
@@ -131,7 +132,11 @@
/// The reasons for the edit.
final Map<FixReasonTarget, FixReasonInfo> fixReasons;
- AtomicEditInfo(this.description, this.fixReasons);
+ /// If the edit is being made due to a hint, the hint in question; otherwise
+ /// `null`.
+ final HintComment hintComment;
+
+ AtomicEditInfo(this.description, this.fixReasons, {this.hintComment});
}
/// An [EditPlan] is a builder capable of accumulating a set of edits to be
@@ -179,6 +184,36 @@
EditPlanner(this.lineInfo, this.sourceText, {this.removeViaComments = false});
/// Creates a new edit plan that consists of executing [innerPlan], and then
+ /// converting the late [hint] into an explicit `late`.
+ NodeProducingEditPlan acceptLateHint(
+ NodeProducingEditPlan innerPlan, HintComment hint,
+ {AtomicEditInfo info}) {
+ var affixPlan = innerPlan is _CommentAffixPlan
+ ? innerPlan
+ : _CommentAffixPlan(innerPlan);
+ var changes = hint.changesToAccept(sourceText, info: info);
+ assert(affixPlan.offset >= _endForChanges(changes));
+ affixPlan.offset = _offsetForChanges(changes);
+ affixPlan._prefixChanges = changes + affixPlan._prefixChanges;
+ return affixPlan;
+ }
+
+ /// Creates a new edit plan that consists of executing [innerPlan], and then
+ /// converting the nullability [hint] into an explicit `?` or `!`.
+ NodeProducingEditPlan acceptNullabilityOrNullCheckHint(
+ NodeProducingEditPlan innerPlan, HintComment hint,
+ {AtomicEditInfo info}) {
+ var affixPlan = innerPlan is _CommentAffixPlan
+ ? innerPlan
+ : _CommentAffixPlan(innerPlan);
+ var changes = hint.changesToAccept(sourceText);
+ assert(affixPlan.end <= _offsetForChanges(changes));
+ affixPlan.end = _endForChanges(changes);
+ affixPlan._postfixChanges += hint.changesToAccept(sourceText, info: info);
+ return affixPlan;
+ }
+
+ /// Creates a new edit plan that consists of executing [innerPlan], and then
/// appending the given [operand], with an intervening binary [operator].
///
/// Optional argument [info] contains information about why the change was
@@ -264,6 +299,21 @@
_PassThroughBuilderImpl(node);
/// Creates a new edit plan that consists of executing [innerPlan], and then
+ /// dropping the given nullability [hint].
+ NodeProducingEditPlan dropNullabilityHint(
+ NodeProducingEditPlan innerPlan, HintComment hint,
+ {AtomicEditInfo info}) {
+ var affixPlan = innerPlan is _CommentAffixPlan
+ ? innerPlan
+ : _CommentAffixPlan(innerPlan);
+ var changes = hint.changesToRemove(sourceText, info: info);
+ assert(affixPlan.end <= _offsetForChanges(changes));
+ affixPlan.end = _endForChanges(changes);
+ affixPlan._postfixChanges += changes;
+ return affixPlan;
+ }
+
+ /// Creates a new edit plan that consists of executing [innerPlan], and then
/// appending an informative ` `, to illustrate that the type is non-nullable.
///
/// Optional argument [info] contains information about why the change was
@@ -603,6 +653,18 @@
return lineInfo.lineStarts[lineNumber - 1];
}
+ int _endForChanges(Map<int, List<AtomicEdit>> changes) {
+ int result;
+ for (var entry in changes.entries) {
+ var end = entry.key;
+ for (var edit in entry.value) {
+ end += edit.length;
+ }
+ if (result == null || end > result) result = end;
+ }
+ return result;
+ }
+
/// Finds the deepest entry in [builderStack] that matches an entry in
/// [ancestryStack], taking advantage of the fact that [builderStack] walks
/// stepwise down the AST, and [ancestryStack] walks stepwise up the AST, with
@@ -665,6 +727,14 @@
return sourceText.substring(offset, end).trimRight().isEmpty;
}
+ int _offsetForChanges(Map<int, List<AtomicEdit>> changes) {
+ int result;
+ for (var key in changes.keys) {
+ if (result == null || key < result) result = key;
+ }
+ return result;
+ }
+
/// If the given [node] maintains a variable-length sequence of child nodes,
/// returns a list containing those child nodes, otherwise returns `null`.
///
@@ -785,6 +855,28 @@
NodeProducingEditPlan finish(EditPlanner planner);
}
+/// [EditPlan] that wraps an inner plan with optional prefix and suffix changes.
+class _CommentAffixPlan extends _NestedEditPlan {
+ Map<int, List<AtomicEdit>> _prefixChanges;
+
+ Map<int, List<AtomicEdit>> _postfixChanges;
+
+ @override
+ int offset;
+
+ @override
+ int end;
+
+ _CommentAffixPlan(NodeProducingEditPlan innerPlan)
+ : offset = innerPlan.offset,
+ end = innerPlan.end,
+ super(innerPlan.sourceNode, innerPlan);
+
+ @override
+ Map<int, List<AtomicEdit>> _getChanges(bool parens) =>
+ _prefixChanges + innerPlan._getChanges(parens) + _postfixChanges;
+}
+
/// Visitor that determines whether a given [AstNode] ends in a cascade.
class _EndsInCascadeVisitor extends UnifyingAstVisitor<void> {
bool endsInCascade = false;
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index bcf9379..da7b040 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -15,6 +15,7 @@
import 'package:nnbd_migration/src/decorated_type.dart';
import 'package:nnbd_migration/src/edit_plan.dart';
import 'package:nnbd_migration/src/fix_builder.dart';
+import 'package:nnbd_migration/src/utilities/hint_utils.dart';
/// Visitor that combines together the changes produced by [FixBuilder] into a
/// concrete set of source code edits using the infrastructure of [EditPlan].
@@ -419,6 +420,8 @@
AtomicEditInfo _addNullCheckInfo;
+ HintComment _addNullCheckHint;
+
DartType _introducesAsType;
AtomicEditInfo _introduceAsInfo;
@@ -439,10 +442,11 @@
DartType get introducesAsType => _introducesAsType;
/// Causes a null check to be added to this expression, with the given [info].
- void addNullCheck(AtomicEditInfo info) {
+ void addNullCheck(AtomicEditInfo info, {HintComment hint}) {
assert(!_addsNullCheck);
_addsNullCheck = true;
_addNullCheckInfo = info;
+ _addNullCheckHint = hint;
}
/// Causes a cast to the given [type] to be added to this expression, with
@@ -467,8 +471,14 @@
FixAggregator aggregator, NodeProducingEditPlan innerPlan) {
var plan = innerPlan;
if (_addsNullCheck) {
- plan = aggregator.planner
- .addUnaryPostfix(plan, TokenType.BANG, info: _addNullCheckInfo);
+ var hint = _addNullCheckHint;
+ if (hint != null) {
+ plan = aggregator.planner.acceptNullabilityOrNullCheckHint(plan, hint,
+ info: _addNullCheckInfo);
+ } else {
+ plan = aggregator.planner
+ .addUnaryPostfix(plan, TokenType.BANG, info: _addNullCheckInfo);
+ }
}
if (_introducesAsType != null) {
plan = aggregator.planner.addBinaryPostfix(
@@ -599,7 +609,7 @@
class NodeChangeForTypeAnnotation extends NodeChange<TypeAnnotation> {
bool _makeNullable = false;
- bool _nullabilityDueToHint = false;
+ HintComment _nullabilityHint;
/// The decorated type of the type annotation, or `null` if there is no
/// decorated type info of interest. If [makeNullable] is `true`, the node
@@ -616,14 +626,15 @@
/// Indicates whether the type should be made nullable by adding a `?`.
bool get makeNullable => _makeNullable;
- /// Indicates whether we are making the type nullable due to a hint.
- bool get makeNullableDueToHint => _nullabilityDueToHint;
+ /// If we are making the type nullable due to a hint, the comment that caused
+ /// it.
+ HintComment get nullabilityHint => _nullabilityHint;
void recordNullability(DecoratedType decoratedType, bool makeNullable,
- {bool nullabilityDueToHint: false}) {
+ {HintComment nullabilityHint}) {
_decoratedType = decoratedType;
_makeNullable = makeNullable;
- _nullabilityDueToHint = nullabilityDueToHint;
+ _nullabilityHint = nullabilityHint;
}
@override
@@ -633,25 +644,35 @@
var typeName = _decoratedType.type.getDisplayString(withNullability: false);
var fixReasons = {FixReasonTarget.root: _decoratedType.node};
if (_makeNullable) {
- NullabilityFixDescription description;
- if (_nullabilityDueToHint) {
- description =
- NullabilityFixDescription.makeTypeNullableDueToHint(typeName);
+ var hint = _nullabilityHint;
+ if (hint != null) {
+ return aggregator.planner.acceptNullabilityOrNullCheckHint(
+ innerPlan, hint,
+ info: AtomicEditInfo(
+ NullabilityFixDescription.makeTypeNullableDueToHint(typeName),
+ fixReasons,
+ hintComment: hint));
} else {
- description = NullabilityFixDescription.makeTypeNullable(typeName);
+ return aggregator.planner.makeNullable(innerPlan,
+ info: AtomicEditInfo(
+ NullabilityFixDescription.makeTypeNullable(typeName),
+ fixReasons));
}
- return aggregator.planner.makeNullable(innerPlan,
- info: AtomicEditInfo(description, fixReasons));
} else {
- NullabilityFixDescription description;
- if (_nullabilityDueToHint) {
- description =
- NullabilityFixDescription.typeNotMadeNullableDueToHint(typeName);
+ var hint = _nullabilityHint;
+ if (hint != null) {
+ return aggregator.planner.dropNullabilityHint(innerPlan, hint,
+ info: AtomicEditInfo(
+ NullabilityFixDescription.typeNotMadeNullableDueToHint(
+ typeName),
+ fixReasons,
+ hintComment: hint));
} else {
- description = NullabilityFixDescription.typeNotMadeNullable(typeName);
+ return aggregator.planner.explainNonNullable(innerPlan,
+ info: AtomicEditInfo(
+ NullabilityFixDescription.typeNotMadeNullable(typeName),
+ fixReasons));
}
- return aggregator.planner.explainNonNullable(innerPlan,
- info: AtomicEditInfo(description, fixReasons));
}
}
}
@@ -664,21 +685,15 @@
/// that should be added. Otherwise `null`.
DartType addExplicitType;
- /// Indicates whether a "late" annotation should be added to this variable
- /// declaration.
- bool addLate = false;
+ /// If a "late" annotation should be added to this variable declaration, the
+ /// hint that caused it. Otherwise `null`.
+ HintComment lateHint;
NodeChangeForVariableDeclarationList() : super._();
@override
EditPlan _apply(VariableDeclarationList node, FixAggregator aggregator) {
List<EditPlan> innerPlans = [];
- if (addLate) {
- innerPlans.add(aggregator.planner.insertText(
- node,
- node.firstTokenAfterCommentAndMetadata.offset,
- [AtomicEdit.insert('late'), AtomicEdit.insert(' ')]));
- }
if (addExplicitType != null) {
var typeText = addExplicitType.getDisplayString(withNullability: true);
if (node.keyword?.keyword == Keyword.VAR) {
@@ -696,7 +711,13 @@
}
}
innerPlans.addAll(aggregator.innerPlansForNode(node));
- return aggregator.planner.passThrough(node, innerPlans: innerPlans);
+ var plan = aggregator.planner.passThrough(node, innerPlans: innerPlans);
+ if (lateHint != null) {
+ plan = aggregator.planner.acceptLateHint(plan, lateHint,
+ info: AtomicEditInfo(NullabilityFixDescription.addLateDueToHint, {},
+ hintComment: lateHint));
+ }
+ return plan;
}
}
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 087a8ae..35177f7 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -33,6 +33,7 @@
import 'package:nnbd_migration/src/edit_plan.dart';
import 'package:nnbd_migration/src/fix_aggregator.dart';
import 'package:nnbd_migration/src/nullability_node.dart';
+import 'package:nnbd_migration/src/utilities/hint_utils.dart';
import 'package:nnbd_migration/src/utilities/permissive_mode.dart';
import 'package:nnbd_migration/src/utilities/resolution_utils.dart';
import 'package:nnbd_migration/src/variables.dart';
@@ -398,13 +399,18 @@
@override
DartType modifyExpressionType(Expression node, DartType type) =>
_wrapExceptions(node, () => type, () {
- if (_fixBuilder._variables.hasNullCheckHint(_fixBuilder.source, node)) {
+ var hint =
+ _fixBuilder._variables.getNullCheckHint(_fixBuilder.source, node);
+ if (hint != null) {
type = _addNullCheck(node, type,
info: AtomicEditInfo(
- NullabilityFixDescription.checkExpressionDueToHint, {
- FixReasonTarget.root:
- FixReason_NullCheckHint(CodeReference.fromAstNode(node))
- }));
+ NullabilityFixDescription.checkExpressionDueToHint,
+ {
+ FixReasonTarget.root:
+ FixReason_NullCheckHint(CodeReference.fromAstNode(node))
+ },
+ hintComment: hint),
+ hint: hint);
}
if (type.isDynamic) return type;
var ancestor = _findNullabilityContextAncestor(node);
@@ -464,7 +470,7 @@
}
DartType _addNullCheck(Expression node, DartType type,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo info, HintComment hint}) {
var checks =
_fixBuilder._variables.expressionChecks(_fixBuilder.source, node);
info ??= checks != null
@@ -472,7 +478,7 @@
NullabilityFixDescription.checkExpression, checks.edges)
: null;
(_fixBuilder._getChange(node) as NodeChangeForExpression)
- .addNullCheck(info);
+ .addNullCheck(info, hint: hint);
_flowAnalysis.nonNullAssert_end(node);
return _fixBuilder._typeSystem.promoteToNonNull(type as TypeImpl);
}
@@ -654,9 +660,10 @@
}
}
}
- if (_fixBuilder._variables.isLateHinted(source, node)) {
+ var lateHint = _fixBuilder._variables.getLateHint(source, node);
+ if (lateHint != null) {
(_fixBuilder._getChange(node) as NodeChangeForVariableDeclarationList)
- .addLate = true;
+ .lateHint = lateHint;
}
super.visitVariableDeclarationList(node);
}
@@ -755,7 +762,7 @@
(_fixBuilder._getChange(node) as NodeChangeForTypeAnnotation)
.recordNullability(
decoratedType, decoratedType.node.isNullable,
- nullabilityDueToHint:
- _fixBuilder._variables.hasNullabilityHint(source, node));
+ nullabilityHint:
+ _fixBuilder._variables.getNullabilityHint(source, node));
}
}
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index d5a1de0..4b19615 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -493,19 +493,22 @@
namedParameters: namedParameters);
}
_variables.recordDecoratedTypeAnnotation(source, node, decoratedType);
- switch (getPostfixHint(node.endToken)) {
- case NullabilityComment.bang:
- _graph.makeNonNullableUnion(
- decoratedType.node, NullabilityCommentOrigin(source, node, false));
- _variables.recordNullabilityHint(source, node);
- break;
- case NullabilityComment.question:
- _graph.makeNullableUnion(
- decoratedType.node, NullabilityCommentOrigin(source, node, true));
- _variables.recordNullabilityHint(source, node);
- break;
- case NullabilityComment.none:
- break;
+ var hint = getPostfixHint(node.endToken);
+ if (hint != null) {
+ switch (hint.kind) {
+ case HintCommentKind.bang:
+ _graph.makeNonNullableUnion(decoratedType.node,
+ NullabilityCommentOrigin(source, node, false));
+ _variables.recordNullabilityHint(source, node, hint);
+ break;
+ case HintCommentKind.question:
+ _graph.makeNullableUnion(
+ decoratedType.node, NullabilityCommentOrigin(source, node, true));
+ _variables.recordNullabilityHint(source, node, hint);
+ break;
+ default:
+ break;
+ }
}
return decoratedType;
}
@@ -539,12 +542,9 @@
node.metadata.accept(this);
var typeAnnotation = node.type;
var type = typeAnnotation?.accept(this);
- switch (getPrefixHint(node.firstTokenAfterCommentAndMetadata)) {
- case PrefixHintComment.late_:
- _variables.recordLateHint(source, node);
- break;
- case PrefixHintComment.none:
- break;
+ var hint = getPrefixHint(node.firstTokenAfterCommentAndMetadata);
+ if (hint != null && hint.kind == HintCommentKind.late_) {
+ _variables.recordLateHint(source, node, hint);
}
for (var variable in node.variables) {
variable.metadata.accept(this);
diff --git a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart b/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart
index d0fa9c8..131fabf 100644
--- a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart
@@ -3,26 +3,36 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
+import 'package:nnbd_migration/src/edit_plan.dart';
-/// Determine if the given token is a nullability hint, and if so, return the
-/// type of nullability hint it is.
-NullabilityComment classifyComment(Token token) {
- if (token is CommentToken) {
- if (token.lexeme == '/*!*/') return NullabilityComment.bang;
- if (token.lexeme == '/*?*/') return NullabilityComment.question;
- }
- return NullabilityComment.none;
-}
-
-/// Determine if the given [token] is followed by a nullability hint, and if so,
-/// return the type of nullability hint it is followed by.
-NullabilityComment getPostfixHint(Token token) {
+/// Determines if the given [token] is followed by a nullability hint, and if
+/// so, returns information about it. Otherwise returns `null`.
+HintComment getPostfixHint(Token token) {
var commentToken = token.next.precedingComments;
- var commentType = classifyComment(commentToken);
- return commentType;
+ if (commentToken != null) {
+ HintCommentKind kind;
+ if (commentToken.lexeme == '/*!*/') {
+ kind = HintCommentKind.bang;
+ } else if (commentToken.lexeme == '/*?*/') {
+ kind = HintCommentKind.question;
+ } else {
+ return null;
+ }
+ return HintComment(
+ kind,
+ token.end,
+ commentToken.offset,
+ commentToken.offset + '/*'.length,
+ commentToken.end - '*/'.length,
+ commentToken.end,
+ commentToken.end);
+ }
+ return null;
}
-PrefixHintComment getPrefixHint(Token token) {
+/// Determines if the given [token] is preceded by a hint, and if so, returns
+/// information about it. Otherwise returns `null`.
+HintComment getPrefixHint(Token token) {
Token commentToken = token.precedingComments;
if (commentToken != null) {
while (true) {
@@ -31,16 +41,150 @@
commentToken = nextComment;
}
var lexeme = commentToken.lexeme;
- if (lexeme.startsWith('/*') && lexeme.endsWith('*/') && lexeme.length > 4) {
- var commentText = lexeme.substring(2, lexeme.length - 2).trim();
- if (commentText == 'late') return PrefixHintComment.late_;
+ if (lexeme.startsWith('/*') &&
+ lexeme.endsWith('*/') &&
+ lexeme.length > 'late'.length) {
+ var commentText =
+ lexeme.substring('/*'.length, lexeme.length - '*/'.length).trim();
+ if (commentText == 'late') {
+ var commentOffset = commentToken.offset;
+ var lateOffset = commentOffset + commentToken.lexeme.indexOf('late');
+ return HintComment(
+ HintCommentKind.late_,
+ commentOffset,
+ commentOffset,
+ lateOffset,
+ lateOffset + 'late'.length,
+ commentToken.end,
+ token.offset);
+ }
}
}
- return PrefixHintComment.none;
+ return null;
}
-/// Types of comments that can influence nullability
-enum NullabilityComment {
+/// Information about a hint found in a source file.
+class HintComment {
+ static final _alphaNumericRegexp = RegExp('[a-zA-Z0-9]');
+
+ /// What kind of hint this is.
+ final HintCommentKind kind;
+
+ /// The file offset of the first character that should be removed if the hint
+ /// is to be removed.
+ final int _removeOffset;
+
+ /// The file offset of the first character of the hint comment itself.
+ final int _commentOffset;
+
+ /// The file offset of the first character that should be kept if the hint is
+ /// to be replaced with the hinted text.
+ final int _keepOffset;
+
+ /// The file offset just beyond the last character that should be kept if the
+ /// hint is to be replaced with the hinted text.
+ final int _keepEnd;
+
+ /// The file offset just beyond the last character of the hint comment itself.
+ final int _commentEnd;
+
+ /// The file offset just beyond the last character that should be removed if
+ /// the hint is to be removed.
+ final int _removeEnd;
+
+ HintComment(this.kind, this._removeOffset, this._commentOffset,
+ this._keepOffset, this._keepEnd, this._commentEnd, this._removeEnd)
+ : assert(_removeOffset <= _commentOffset),
+ assert(_commentOffset < _keepOffset),
+ assert(_keepOffset < _keepEnd),
+ assert(_keepEnd < _commentEnd),
+ assert(_commentEnd <= _removeEnd);
+
+ /// Creates the changes necessary to accept the given hint (replace it with
+ /// its contents and fix up whitespace).
+ Map<int, List<AtomicEdit>> changesToAccept(String sourceText,
+ {AtomicEditInfo info}) {
+ bool prependSpace = false;
+ bool appendSpace = false;
+ var removeOffset = _removeOffset;
+ var removeEnd = _removeEnd;
+ if (_isAlphaNumericBeforeOffset(sourceText, removeOffset) &&
+ _isAlphaNumericAtOffset(sourceText, _keepOffset)) {
+ if (sourceText[removeOffset] == ' ') {
+ // We can just keep this space.
+ removeOffset++;
+ } else {
+ prependSpace = true;
+ }
+ }
+ if (_isAlphaNumericBeforeOffset(sourceText, _keepEnd) &&
+ _isAlphaNumericAtOffset(sourceText, removeEnd)) {
+ if (sourceText[removeEnd - 1] == ' ') {
+ // We can just keep this space.
+ removeEnd--;
+ } else {
+ appendSpace = true;
+ }
+ }
+
+ return {
+ removeOffset: [
+ if (prependSpace) AtomicEdit.insert(' '),
+ AtomicEdit.delete(_keepOffset - removeOffset, info: info)
+ ],
+ _keepEnd: [AtomicEdit.delete(removeEnd - _keepEnd, info: info)],
+ if (appendSpace) removeEnd: [AtomicEdit.insert(' ')]
+ };
+ }
+
+ /// Creates the changes necessary to remove the given hint (and fix up
+ /// whitespace).
+ Map<int, List<AtomicEdit>> changesToRemove(String sourceText,
+ {AtomicEditInfo info}) {
+ bool appendSpace = false;
+ var removeOffset = this._removeOffset;
+ if (_isAlphaNumericBeforeOffset(sourceText, removeOffset) &&
+ _isAlphaNumericAtOffset(sourceText, _removeEnd)) {
+ if (sourceText[removeOffset] == ' ') {
+ // We can just keep this space.
+ removeOffset++;
+ } else {
+ appendSpace = true;
+ }
+ }
+ return {
+ removeOffset: [
+ AtomicEdit.delete(_removeEnd - removeOffset, info: info),
+ if (appendSpace) AtomicEdit.insert(' ')
+ ]
+ };
+ }
+
+ /// Creates the changes necessary to replace the given hint with a different
+ /// hint.
+ Map<int, List<AtomicEdit>> changesToReplace(
+ String sourceText, String replacement,
+ {AtomicEditInfo info}) {
+ return {
+ _commentOffset: [
+ AtomicEdit.replace(_commentEnd - _commentOffset, replacement,
+ info: info)
+ ]
+ };
+ }
+
+ static bool _isAlphaNumericAtOffset(String sourceText, int offset) {
+ return offset < sourceText.length &&
+ _alphaNumericRegexp.hasMatch(sourceText[offset]);
+ }
+
+ static bool _isAlphaNumericBeforeOffset(String sourceText, int offset) {
+ return offset > 0 && _alphaNumericRegexp.hasMatch(sourceText[offset - 1]);
+ }
+}
+
+/// Types of hint comments
+enum HintCommentKind {
/// The comment `/*!*/`, which indicates that the type should not have a `?`
/// appended.
bang,
@@ -49,16 +193,7 @@
/// appended.
question,
- /// No special comment.
- none,
-}
-
-/// Types of comments that can appear before a token
-enum PrefixHintComment {
/// The comment `/*late*/`, which indicates that the variable declaration
/// should be late.
late_,
-
- /// No special comment
- none,
}
diff --git a/pkg/nnbd_migration/lib/src/variables.dart b/pkg/nnbd_migration/lib/src/variables.dart
index 6b751af..1d60f68 100644
--- a/pkg/nnbd_migration/lib/src/variables.dart
+++ b/pkg/nnbd_migration/lib/src/variables.dart
@@ -26,6 +26,7 @@
import 'package:nnbd_migration/src/nullability_node.dart';
import 'package:nnbd_migration/src/nullability_node_target.dart';
import 'package:nnbd_migration/src/postmortem_file.dart';
+import 'package:nnbd_migration/src/utilities/hint_utils.dart';
/// Data structure used by [Variables.spanForUniqueIdentifier] to return an
/// offset/end pair.
@@ -56,11 +57,11 @@
final _expressionChecks = <Source, Map<int, ExpressionChecks>>{};
- final _lateHints = <Source, Set<int>>{};
+ final _lateHints = <Source, Map<int, HintComment>>{};
- final _nullCheckHints = <Source, Set<int>>{};
+ final _nullCheckHints = <Source, Map<int, HintComment>>{};
- final _nullabilityHints = <Source, Set<int>>{};
+ final _nullabilityHints = <Source, Map<int, HintComment>>{};
final _unnecessaryCasts = <Source, Set<int>>{};
@@ -166,24 +167,26 @@
ConditionalDiscard getConditionalDiscard(Source source, AstNode node) =>
(_conditionalDiscards[source] ?? {})[node.offset];
- /// Queries whether the given [node] is preceded by a `/*?*/` hint. See
+ /// If the given [node] is preceded by a `/*late*/` hint, returns the
+ /// HintComment for it; otherwise returns `null`. See [recordLateHint].
+ HintComment getLateHint(Source source, VariableDeclarationList node) {
+ return (_lateHints[source] ?? {})[node.offset];
+ }
+
+ /// If the given [node] is followed by a `/*?*/` or /*!*/ hint, returns the
+ /// HintComment for it; otherwise returns `null`. See
/// [recordNullabilityHint].
- bool hasNullabilityHint(Source source, TypeAnnotation node) {
- return (_nullabilityHints[source] ?? {})
- .contains(uniqueIdentifierForSpan(node.offset, node.end));
+ HintComment getNullabilityHint(Source source, TypeAnnotation node) {
+ return (_nullabilityHints[source] ??
+ {})[uniqueIdentifierForSpan(node.offset, node.end)];
}
- /// Queries whether the given [expression] is followed by a null check hint
- /// (`/*!*/`). See [recordNullCheckHint].
- bool hasNullCheckHint(Source source, Expression expression) {
- return (_nullCheckHints[source] ?? {})
- .contains(uniqueIdentifierForSpan(expression.offset, expression.end));
- }
-
- /// Queries whether the given [node] is preceded by a `/*late*/` hint. See
- /// [recordLateHint].
- bool isLateHinted(Source source, VariableDeclarationList node) {
- return (_lateHints[source] ?? {}).contains(node.offset);
+ /// If the given [expression] is followed by a null check hint (`/*!*/`),
+ /// returns the HintComment for it; otherwise returns `null`. See
+ /// [recordNullCheckHint].
+ HintComment getNullCheckHint(Source source, Expression expression) {
+ return (_nullCheckHints[source] ??
+ {})[(uniqueIdentifierForSpan(expression.offset, expression.end))];
}
/// Records conditional discard information for the given AST node (which is
@@ -238,21 +241,25 @@
}
/// Records that the given [node] was preceded by a `/*late*/` hint.
- void recordLateHint(Source source, VariableDeclarationList node) {
- (_lateHints[source] ??= {}).add(node.offset);
+ void recordLateHint(
+ Source source, VariableDeclarationList node, HintComment hint) {
+ (_lateHints[source] ??= {})[node.offset] = hint;
}
/// Records that the given [node] was followed by a `/*?*/` or `/*!*/` hint.
- void recordNullabilityHint(Source source, TypeAnnotation node) {
- (_nullabilityHints[source] ??= {})
- .add(uniqueIdentifierForSpan(node.offset, node.end));
+ void recordNullabilityHint(
+ Source source, TypeAnnotation node, HintComment hintComment) {
+ (_nullabilityHints[source] ??=
+ {})[uniqueIdentifierForSpan(node.offset, node.end)] = hintComment;
}
/// Records that the given [expression] is followed by a null check hint
/// (`/*!*/`), for later recall by [hasNullCheckHint].
- void recordNullCheckHint(Source source, Expression expression) {
- (_nullCheckHints[source] ??= {})
- .add(uniqueIdentifierForSpan(expression.offset, expression.end));
+ void recordNullCheckHint(
+ Source source, Expression expression, HintComment hintComment) {
+ (_nullCheckHints[source] ??=
+ {})[uniqueIdentifierForSpan(expression.offset, expression.end)] =
+ hintComment;
}
/// Records the fact that prior to migration, an unnecessary cast existed at
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index b69cf77..e4d7a54 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -224,7 +224,7 @@
}
''';
var expected = '''
-class C<T extends num?/*?*/> {}
+class C<T extends num?> {}
void main() {
C<num> c = C();
@@ -306,7 +306,7 @@
}
''';
var expected = '''
-class B<E> implements List<E?/*?*/> {
+class B<E> implements List<E?> {
final C c;
B(this.c);
B<T> cast<T>() => c._castFrom<E, T>(this);
@@ -447,7 +447,7 @@
}
''';
var expected = '''
-void f(int/*!*/ i) {}
+void f(int i) {}
void g(bool b, int? i) {
if (b) f(i!);
}
@@ -466,7 +466,7 @@
''';
var expected = '''
void _f() {
- int?/*?*/ i = 0;
+ int? i = 0;
}
''';
await _checkSingleFileChanges(content, expected);
@@ -609,8 +609,8 @@
num f4<T extends num>(bool b, num x, T y) => b ? x : y;
void main() {
- int? x1 = f1<int?/*?*/>(true, 0, null) as int?;
- int? x2 = f2<int/*!*/>(true, 0, null) as int?;
+ int? x1 = f1<int?>(true, 0, null) as int?;
+ int? x2 = f2<int>(true, 0, null) as int?;
int? x3 = f3<int>(true, null, 0) as int?;
int x4 = f4<int>(true, 0, 0) as int;
}
@@ -798,7 +798,7 @@
''';
var expected = '''
class C {
- int?/*?*/ f = 0;
+ int? f = 0;
}
int? f(C c) => c.f;
''';
@@ -928,7 +928,7 @@
abstract class C<T> {
T getValue();
}
-int? f(C<int?/*?*/> x) => x.getValue();
+int? f(C<int?> x) => x.getValue();
''';
await _checkSingleFileChanges(content, expected);
}
@@ -1245,7 +1245,7 @@
// Note: https://github.com/dart-lang/sdk/issues/40471 tracks the fact that
// we ought to alert the user to the presence of such casts.
var expected = '''
-void f(int/*!*/ Function(int?) callback) {
+void f(int Function(int?) callback) {
callback(null);
}
int? g(int? x) => x;
@@ -1332,16 +1332,14 @@
nullNonNull = hardToNullNonNull
}
''';
-
- // TODO(paulberry): remove the /*!*/, /*?*/ comments on migration.
var expected = '''
void f(dynamic a) {
List<int> hardToNonNullNonNull = a;
List<int?> hardToNullNonNull = a;
List<int>? hardToNonNullNull = a;
- List<int/*!*/>/*!*/ nonNullNonNull;
- List<int/*?*/>/*!*/ nullNonNull;
- List<int/*!*/>/*?*/ nonNullNull;
+ List<int> nonNullNonNull;
+ List<int?> nullNonNull;
+ List<int>? nonNullNull;
nonNullNonNull = hardToNonNullNonNull
nonNullNull = hardToNonNullNull
nullNonNull = hardToNullNonNull
@@ -1375,10 +1373,8 @@
var content = '''
List<int> f(Iterable<int/*?*/> a) => a;
''';
-
- // TODO(paulberry): remove the /*!*/, /*?*/ comments on migration.
var expected = '''
-List<int?> f(Iterable<int/*?*/> a) => a;
+List<int?> f(Iterable<int?> a) => a;
''';
await _checkSingleFileChanges(content, expected);
}
@@ -1406,8 +1402,6 @@
abstract class C<A, B> implements List<A> {}
C<int, num> f(List<int> a) => a;
''';
-
- // TODO(paulberry): remove the /*!*/, /*?*/ comments on migration.
var expected = '''
abstract class C<A, B> implements List<A> {}
C<int, num?> f(List<int> a) => a;
@@ -1509,7 +1503,7 @@
List<int> _f(List<int/*!*/> xs) => [for(var x in xs) if (x == null) 1];
''';
var expected = '''
-List<int> _f(List<int/*!*/> xs) => [];
+List<int> _f(List<int> xs) => [];
''';
await _checkSingleFileChanges(content, expected);
}
@@ -1692,7 +1686,7 @@
int f(int/*?*/ i) => i + 1;
''';
var expected = '''
-int f(int?/*?*/ i) => i! + 1;
+int f(int? i) => i! + 1;
''';
await _checkSingleFileChanges(content, expected);
}
@@ -1702,7 +1696,7 @@
int f(int/*?*/ i) => i/*!*/;
''';
var expected = '''
-int f(int?/*?*/ i) => i!/*!*/;
+int f(int? i) => i!;
''';
await _checkSingleFileChanges(content, expected, removeViaComments: true);
}
@@ -1714,7 +1708,7 @@
// The user requested a null check so we should add it even if it's not
// required to avoid compile errors.
var expected = '''
-int?/*?*/ f(int?/*?*/ i) => i!/*!*/;
+int? f(int? i) => i!;
''';
await _checkSingleFileChanges(content, expected, removeViaComments: true);
}
@@ -1723,15 +1717,13 @@
var content = 'int/*?*/ f() => 1/*!*/;';
// The user requested a null check so we should add it even if it's not
// required to avoid compile errors.
- var expected = 'int?/*?*/ f() => 1!/*!*/;';
+ var expected = 'int? f() => 1!;';
await _checkSingleFileChanges(content, expected, removeViaComments: true);
}
Future<void> test_expression_bang_hint_with_cast() async {
var content = 'int f(Object/*?*/ o) => o/*!*/;';
- // TODO(paulberry): it would be better to remove the `/*` and `*/` so we
- // would be left with `o! as int;`
- var expected = 'int f(Object?/*?*/ o) => o! as int/*!*/;';
+ var expected = 'int f(Object? o) => o! as int;';
await _checkSingleFileChanges(content, expected);
}
@@ -1866,7 +1858,7 @@
''';
var expected = '''
class C {
- int/*!*/ i;
+ int i;
C(int this.i);
}
void f(int? i, bool b) {
@@ -2283,7 +2275,7 @@
var expected = '''
abstract class C {
void Function(int?) f();
- int?/*?*/ Function() g();
+ int? Function() g();
}
int? test(C c) {
c.f()(null);
@@ -2307,7 +2299,7 @@
var expected = '''
abstract class C {
void Function(int?) get f;
- int?/*?*/ Function() get g;
+ int? Function() get g;
}
int? test(C c) {
c.f(null);
@@ -2382,9 +2374,9 @@
import 'dart:async';
void f(
FutureOr<int> foi1,
- FutureOr<int?/*?*/> foi2,
- FutureOr<int>?/*?*/ foi3,
- FutureOr<int?/*?*/>?/*?*/ foi4
+ FutureOr<int?> foi2,
+ FutureOr<int>? foi3,
+ FutureOr<int?>? foi4
) {
int i1 = foi1 as int;
int? i2 = foi2 as int?;
@@ -2486,7 +2478,7 @@
''';
var expected = '''
class C {
- final Comparator<int/*!*/> comparison;
+ final Comparator<int> comparison;
C(int Function(int, int) comparison) : comparison = comparison;
void test() {
comparison(f()!, f()!);
@@ -2593,7 +2585,7 @@
final T? value;
A(this.value);
}
-class C implements A<String/*!*/> {
+class C implements A<String> {
String? get value => false ? "y" : null;
}
''';
@@ -2630,7 +2622,7 @@
x.add(null);
}
void g() {
- f(<int/*!*/>[]);
+ f(<int>[]);
}
''';
await _checkSingleFileChanges(content, expected);
@@ -2668,7 +2660,7 @@
// unnecessarily force B.f's parameter type to be nullable.
var expected = '''
abstract class A {
- void f(int?/*?*/ i);
+ void f(int? i);
}
abstract class B {
void f(int i);
@@ -2697,7 +2689,7 @@
// B.f's return type to be nullable.
var expected = '''
abstract class A {
- int?/*?*/ f();
+ int? f();
}
abstract class B {
int f();
@@ -2860,8 +2852,8 @@
}
''';
var expected = '''
-class C<T extends Object/*!*/> {
- C(T/*!*/ t);
+class C<T extends Object> {
+ C(T t);
}
main() {
C<int> c = C<int>(null!);
@@ -2882,7 +2874,7 @@
''';
var expected = '''
class C<T> {
- C(T/*!*/ t);
+ C(T t);
}
main() {
C<int?> c = C<int?>(null);
@@ -2901,8 +2893,8 @@
}
''';
var expected = '''
-class C<T extends Object/*!*/> {
- C(T/*!*/ t);
+class C<T extends Object> {
+ C(T t);
}
main() {
C<int> c = C(null!);
@@ -2923,7 +2915,7 @@
''';
var expected = '''
class C<T> {
- C(T/*!*/ t);
+ C(T t);
}
main() {
C<int?> c = C(null);
@@ -3041,12 +3033,10 @@
int g() => x;
}
''';
- // TODO(paulberry): it would be better to just replace the comment with the
- // word `late`.
var expected = '''
class C {
C();
- /*late*/ late int x;
+ late int x;
f() {
x = 1;
}
@@ -3066,11 +3056,9 @@
int g() => x;
}
''';
- // TODO(paulberry): it would be better to just replace the comment with the
- // word `late`.
var expected = '''
class C {
- /*late*/ late int x;
+ late int x;
f() {
x = 1;
}
@@ -3093,11 +3081,9 @@
return 0;
}
''';
- // TODO(paulberry): it would be better to just replace the comment with the
- // word `late`.
var expected = '''
int f(bool b1, bool b2) {
- /*late*/ late int x;
+ late int x;
if (b1) {
x = 1;
}
@@ -3120,11 +3106,9 @@
int g() => x;
}
''';
- // TODO(paulberry): it would be better to just replace the comment with the
- // word `late`.
var expected = '''
class C {
- static /*late*/ late int x;
+ static late int x;
f() {
x = 1;
}
@@ -3142,10 +3126,8 @@
}
int g() => x;
''';
- // TODO(paulberry): it would be better to just replace the comment with the
- // word `late`.
var expected = '''
-/*late*/ late int x;
+late int x;
f() {
x = 1;
}
@@ -3396,7 +3378,7 @@
}
''';
var expected = '''
-T f<T extends Object/*!*/>(T/*!*/ t) => t;
+T f<T extends Object>(T t) => t;
void g() {
int x = f<int>(null!);
}
@@ -3413,7 +3395,7 @@
}
''';
var expected = '''
-T f<T>(T/*!*/ t) => t;
+T f<T>(T t) => t;
void g() {
int? x = f<int?>(null);
}
@@ -3446,7 +3428,7 @@
}
''';
var expected = '''
-T f<T extends Object/*!*/>(T/*!*/ t) => t;
+T f<T extends Object>(T t) => t;
void g() {
int x = f(null!);
}
@@ -3463,7 +3445,7 @@
}
''';
var expected = '''
-T f<T>(T/*!*/ t) => t;
+T f<T>(T t) => t;
void g() {
int? x = f(null);
}
@@ -3781,10 +3763,10 @@
''';
var expected = '''
abstract class C {
- void f(List<int/*!*/> x, int y) {
+ void f(List<int> x, int y) {
x.add(y);
}
- int?/*?*/ g();
+ int? g();
void test() {
f(<int>[], g()!);
}
@@ -3806,7 +3788,7 @@
// safely passed to f.
var expected = '''
Iterable<int> f(List<int?> x) => x.map(g as int Function(int?));
-int g(int/*!*/ x) => x + 1;
+int g(int x) => x + 1;
main() {
f([null]);
}
@@ -3926,8 +3908,8 @@
class _C {
f() {}
}
-_C g(int/*!*/ i) => _C();
-test(int?/*?*/ j) {
+_C g(int i) => _C();
+test(int? j) {
g(j!)..f();
}
''';
@@ -3943,7 +3925,7 @@
}
''';
var expected = '''
-abstract class C<E, T extends Iterable<E>?/*?*/> {
+abstract class C<E, T extends Iterable<E>?> {
void f(T iter) {
for(var i in iter!) {}
}
@@ -4102,7 +4084,7 @@
''';
var expected = '''
abstract class Base {
- int/*!*/ f();
+ int f();
}
class Derived extends Base {
int f() => g()!;
@@ -4253,7 +4235,7 @@
D<T> operator-();
}
class D<U> {}
-D<int?> test(C<int?/*?*/> c) => -c;
+D<int?> test(C<int?> c) => -c;
''';
await _checkSingleFileChanges(content, expected);
}
@@ -4324,7 +4306,7 @@
// `x ??= [0]` promotes x from List<int?>? to List<int?>. Since there is
// still a `?` on the `int`, `x[0]` must be null checked.
var expected = '''
-int/*!*/ f(List<int?/*?*/>?/*?*/ x) {
+int f(List<int?>? x) {
x ??= [0];
return x[0]!;
}
@@ -4347,7 +4329,7 @@
// return type of the function literal. As a result, the reference to `x`
// in the function literal is null checked.
var expected = '''
-void f(int/*!*/ Function(int?) callback) {
+void f(int Function(int?) callback) {
callback(null);
}
void test() {
@@ -4499,7 +4481,7 @@
Future<void> test_remove_question_from_question_dot() async {
var content = '_f(int/*!*/ i) => i?.isEven;';
- var expected = '_f(int/*!*/ i) => i.isEven;';
+ var expected = '_f(int i) => i.isEven;';
await _checkSingleFileChanges(content, expected);
}
@@ -4512,16 +4494,16 @@
''';
var expected = '''
class C {
- int?/*?*/ i;
+ int? i;
}
-int/*!*/ f(C/*!*/ c) => c.i!;
+int f(C c) => c.i!;
''';
await _checkSingleFileChanges(content, expected);
}
Future<void> test_remove_question_from_question_dot_method() async {
var content = '_f(int/*!*/ i) => i?.abs();';
- var expected = '_f(int/*!*/ i) => i.abs();';
+ var expected = '_f(int i) => i.abs();';
await _checkSingleFileChanges(content, expected);
}
@@ -4534,9 +4516,9 @@
''';
var expected = '''
class C {
- int/*!*/ i;
+ int i;
}
-bool?/*?*/ f(C?/*?*/ c) => c?.i.isEven;
+bool? f(C? c) => c?.i.isEven;
''';
await _checkSingleFileChanges(content, expected);
}
@@ -4910,7 +4892,7 @@
var expected = '''
typedef F = Function(int?);
-F/*!*/ _f;
+F _f;
f() {
_f(null);
@@ -4975,7 +4957,7 @@
var expected = '''
typedef F = Function<T>(T);
-F/*!*/ _f;
+F _f;
f() {
_f<int?>(null);
@@ -4997,7 +4979,7 @@
var expected = '''
typedef F<R> = Function<T>(T);
-F<Object>/*!*/ _f;
+F<Object> _f;
f() {
_f<int?>(null);
@@ -5019,7 +5001,7 @@
var expected = '''
typedef F<T> = Function(T);
-F<int?>/*!*/ _f;
+F<int?> _f;
f() {
_f(null);
@@ -5041,7 +5023,7 @@
var expected = '''
typedef F<T> = Function(T);
-F<int?>/*!*/ _f;
+F<int?> _f;
f() {
_f(null);
@@ -5077,7 +5059,7 @@
var expected = '''
typedef F(int? x);
-F/*!*/ _f;
+F _f;
f() {
_f(null);
@@ -5127,7 +5109,7 @@
var expected = '''
typedef F<T>(T t);
-F<int?>/*!*/ _f;
+F<int?> _f;
f() {
_f(null);
@@ -5149,7 +5131,7 @@
var expected = '''
typedef F<T>(T t);
-F<int?>/*!*/ _f;
+F<int?> _f;
f() {
_f(null);
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index bb1e7e2..8e55074 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -566,7 +566,7 @@
}
bool hasNullCheckHint(Expression expression) =>
- variables.hasNullCheckHint(testSource, expression);
+ variables.getNullCheckHint(testSource, expression) != null;
Future<void> test_already_migrated_field() async {
await analyze('''
diff --git a/pkg/nnbd_migration/test/edit_plan_test.dart b/pkg/nnbd_migration/test/edit_plan_test.dart
index 120b97c..99c9b49 100644
--- a/pkg/nnbd_migration/test/edit_plan_test.dart
+++ b/pkg/nnbd_migration/test/edit_plan_test.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:nnbd_migration/src/edit_plan.dart';
+import 'package:nnbd_migration/src/utilities/hint_utils.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -58,6 +59,62 @@
NodeProducingEditPlan extract(AstNode inner, AstNode outer) =>
planner.extract(outer, planner.passThrough(inner));
+ Future<void> test_acceptLateHint() async {
+ var code = '/* late */ int x = 0;';
+ await analyze(code);
+ var hint = getPrefixHint(findNode.simple('int').token);
+ var changes = checkPlan(
+ planner.acceptLateHint(
+ planner.passThrough(findNode.simple('int')), hint),
+ 'late int x = 0;');
+ expect(changes.keys, unorderedEquals([0, 7]));
+ expect(changes[7], hasLength(1));
+ expect(changes[7][0].length, 3);
+ }
+
+ Future<void> test_acceptLateHint_space_needed_after() async {
+ var code = '/* late */int x = 0;';
+ await analyze(code);
+ var hint = getPrefixHint(findNode.simple('int').token);
+ checkPlan(
+ planner.acceptLateHint(
+ planner.passThrough(findNode.simple('int')), hint),
+ 'late int x = 0;');
+ }
+
+ Future<void> test_acceptLateHint_space_needed_before() async {
+ var code = '@deprecated/* late */ int x = 0;';
+ await analyze(code);
+ var hint = getPrefixHint(findNode.simple('int').token);
+ checkPlan(
+ planner.acceptLateHint(
+ planner.passThrough(findNode.simple('int')), hint),
+ '@deprecated late int x = 0;');
+ }
+
+ Future<void> test_acceptNullabilityOrNullCheckHint() async {
+ var code = 'int /*?*/ x = 0;';
+ await analyze(code);
+ var intRef = findNode.simple('int');
+ var typeName = planner.passThrough(intRef);
+ checkPlan(
+ planner.acceptNullabilityOrNullCheckHint(
+ typeName, getPostfixHint(intRef.token)),
+ 'int? x = 0;');
+ }
+
+ Future<void> test_acceptNullabilityOrNullCheckHint_inside_extract() async {
+ var code = 'f(x) => 3 * x /*!*/ * 4;';
+ await analyze(code);
+ var xRef = findNode.simple('x /*');
+ checkPlan(
+ planner.extract(
+ xRef.parent.parent,
+ planner.acceptNullabilityOrNullCheckHint(
+ planner.passThrough(xRef), getPostfixHint(xRef.token))),
+ 'f(x) => x!;');
+ }
+
Future<void> test_addBinaryPostfix_assignment_right_associative() async {
await analyze('_f(a, b, c) => a = b;');
// Admittedly this is sort of a bogus test case, since the code it produces
@@ -288,6 +345,50 @@
}
}
+ Future<void> test_dropNullabilityHint() async {
+ var code = 'int /*!*/ x = 0;';
+ await analyze(code);
+ var intRef = findNode.simple('int');
+ var typeName = planner.passThrough(intRef);
+ checkPlan(
+ planner.dropNullabilityHint(typeName, getPostfixHint(intRef.token)),
+ 'int x = 0;');
+ }
+
+ Future<void> test_dropNullabilityHint_space_before_must_be_kept() async {
+ var code = 'int /*!*/x = 0;';
+ await analyze(code);
+ var intRef = findNode.simple('int');
+ var typeName = planner.passThrough(intRef);
+ var changes = checkPlan(
+ planner.dropNullabilityHint(typeName, getPostfixHint(intRef.token)),
+ 'int x = 0;');
+ expect(changes.keys, unorderedEquals([code.indexOf('/*')]));
+ }
+
+ Future<void> test_dropNullabilityHint_space_needed() async {
+ var code = 'int/*!*/x = 0;';
+ await analyze(code);
+ var intRef = findNode.simple('int');
+ var typeName = planner.passThrough(intRef);
+ checkPlan(
+ planner.dropNullabilityHint(typeName, getPostfixHint(intRef.token)),
+ 'int x = 0;');
+ }
+
+ Future<void> test_dropNullabilityHint_tight_no_space_needed() async {
+ // We try to minimize how much we alter the source code, so we don't insert
+ // a space in this example even though it would look prettier to do so.
+ var code = 'void Function()/*!*/x = () {};';
+ await analyze(code);
+ var functionType = findNode.genericFunctionType('Function');
+ var typeName = planner.passThrough(functionType);
+ checkPlan(
+ planner.dropNullabilityHint(
+ typeName, getPostfixHint(functionType.endToken)),
+ 'void Function()x = () {};');
+ }
+
Future<void> test_explainNonNullable() async {
await analyze('int x = 0;');
checkPlan(
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index dd306f7..8b320b7 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -49,13 +49,12 @@
static final isMakeNullable = TypeMatcher<NodeChangeForTypeAnnotation>()
.having((c) => c.makeNullable, 'makeNullable', true)
- .having((c) => c.makeNullableDueToHint, 'makeNullableDueToHint', false);
+ .having((c) => c.nullabilityHint, 'nullabilityHint', isNull);
static final isMakeNullableDueToHint =
TypeMatcher<NodeChangeForTypeAnnotation>()
.having((c) => c.makeNullable, 'makeNullable', true)
- .having(
- (c) => c.makeNullableDueToHint, 'makeNullableDueToHint', true);
+ .having((c) => c.nullabilityHint, 'nullabilityHint', isNotNull);
static const isEdge = TypeMatcher<NullabilityEdge>();
diff --git a/pkg/nnbd_migration/test/instrumentation_test.dart b/pkg/nnbd_migration/test/instrumentation_test.dart
index e6c1681..67cce5e 100644
--- a/pkg/nnbd_migration/test/instrumentation_test.dart
+++ b/pkg/nnbd_migration/test/instrumentation_test.dart
@@ -149,6 +149,13 @@
migration.finish();
}
+ void assertEdit(AtomicEdit edit,
+ {dynamic description = anything, dynamic fixReasons = anything}) {
+ var info = edit.info;
+ expect(info.description, description);
+ expect(info.fixReasons, fixReasons);
+ }
+
Future<void> test_explicitTypeNullability() async {
var content = '''
int x = 1;
@@ -209,79 +216,78 @@
}
Future<void> test_fix_reason_add_required_function() async {
- await analyze('_f({int/*!*/ i) {}');
+ var content = '_f({int/*!*/ i) {}';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description,
- NullabilityFixDescription.addRequired(null, '_f', 'i'));
- expect(info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- }
- }
+ var intPos = content.indexOf('int');
+ var commentPos = content.indexOf('/*');
+ expect(changes.keys, unorderedEquals([intPos, commentPos]));
+ assertEdit(changes[intPos].single,
+ description: NullabilityFixDescription.addRequired(null, '_f', 'i'),
+ fixReasons: {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ });
}
Future<void> test_fix_reason_add_required_method() async {
- await analyze('class C { _f({int/*!*/ i) {} }');
+ var content = 'class C { _f({int/*!*/ i) {} }';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description,
- NullabilityFixDescription.addRequired('C', '_f', 'i'));
- expect(info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- }
- }
+ var intPos = content.indexOf('int');
+ var commentPos = content.indexOf('/*');
+ expect(changes.keys, unorderedEquals([intPos, commentPos]));
+ assertEdit(changes[intPos].single,
+ description: NullabilityFixDescription.addRequired('C', '_f', 'i'),
+ fixReasons: {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ });
}
Future<void> test_fix_reason_discard_condition() async {
- await analyze('''
+ var content = '''
_f(int/*!*/ i) {
if (i != null) {
return i;
}
}
-''');
+''';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description, NullabilityFixDescription.discardCondition);
- expect(info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- }
- }
+ var commentPos = content.indexOf('/*');
+ var ifPos = content.indexOf('if');
+ var afterReturnPos = content.indexOf('i;') + 2;
+ expect(changes.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
+ var expectedFixReasons = {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ };
+ assertEdit(changes[ifPos].single,
+ description: NullabilityFixDescription.discardCondition,
+ fixReasons: expectedFixReasons);
+ assertEdit(changes[afterReturnPos].single,
+ description: NullabilityFixDescription.discardCondition,
+ fixReasons: expectedFixReasons);
}
Future<void> test_fix_reason_discard_condition_no_block() async {
- await analyze('''
+ var content = '''
_f(int/*!*/ i) {
if (i != null) return i;
}
-''');
+''';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description, NullabilityFixDescription.discardCondition);
- expect(info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- }
- }
+ var commentPos = content.indexOf('/*');
+ var ifPos = content.indexOf('if');
+ expect(changes.keys, unorderedEquals([commentPos, ifPos]));
+ assertEdit(changes[ifPos].single,
+ description: NullabilityFixDescription.discardCondition,
+ fixReasons: {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ });
}
Future<void> test_fix_reason_discard_else() async {
- await analyze('''
+ var content = '''
_f(int/*!*/ i) {
if (i != null) {
return i;
@@ -289,47 +295,46 @@
return 'null';
}
}
-''');
+''';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, hasLength(2));
- // Change #1: drop the if-condition.
- var dropCondition = changes[findNode.statement('if').offset].single;
- expect(dropCondition.isDeletion, true);
- expect(dropCondition.info.description,
- NullabilityFixDescription.discardCondition);
- expect(dropCondition.info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- // Change #2: drop the else.
- var dropElse = changes[findNode.statement('return i').end].single;
- expect(dropElse.isDeletion, true);
- expect(dropElse.info.description, NullabilityFixDescription.discardElse);
- expect(dropElse.info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
+ var commentPos = content.indexOf('/*');
+ var ifPos = content.indexOf('if');
+ var afterReturnPos = content.indexOf('i;') + 2;
+ expect(changes.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
+ var expectedFixReasons = {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ };
+ assertEdit(changes[ifPos].single,
+ description: NullabilityFixDescription.discardCondition,
+ fixReasons: expectedFixReasons);
+ assertEdit(changes[afterReturnPos].single,
+ description: NullabilityFixDescription.discardElse,
+ fixReasons: expectedFixReasons);
}
Future<void> test_fix_reason_discard_else_empty_then() async {
- await analyze('''
+ var content = '''
_f(int/*!*/ i) {
if (i != null) {} else {
return 'null';
}
}
-''');
+''';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description, NullabilityFixDescription.discardIf);
- expect(info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- }
- }
+ var commentPos = content.indexOf('/*');
+ var bodyPos = content.indexOf('i) {') + 4;
+ expect(changes.keys, unorderedEquals([commentPos, bodyPos]));
+ assertEdit(changes[bodyPos].single,
+ description: NullabilityFixDescription.discardIf,
+ fixReasons: {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ });
}
Future<void> test_fix_reason_discard_then() async {
- await analyze('''
+ var content = '''
_f(int/*!*/ i) {
if (i == null) {
return 'null';
@@ -337,39 +342,42 @@
return i;
}
}
-''');
+''';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description, NullabilityFixDescription.discardThen);
- expect(info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- }
- }
+ var commentPos = content.indexOf('/*');
+ var ifPos = content.indexOf('if');
+ var afterReturnPos = content.indexOf('i;') + 2;
+ expect(changes.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
+ var expectedFixReasons = {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ };
+ assertEdit(changes[ifPos].single,
+ description: NullabilityFixDescription.discardThen,
+ fixReasons: expectedFixReasons);
+ assertEdit(changes[afterReturnPos].single,
+ description: NullabilityFixDescription.discardThen,
+ fixReasons: expectedFixReasons);
}
Future<void> test_fix_reason_discard_then_no_else() async {
- await analyze('''
+ var content = '''
_f(int/*!*/ i) {
if (i == null) {
return 'null';
}
}
-''');
+''';
+ await analyze(content);
var intAnnotation = findNode.typeAnnotation('int');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description, NullabilityFixDescription.discardIf);
- expect(info.fixReasons[FixReasonTarget.root],
- same(explicitTypeNullability[intAnnotation]));
- }
- }
+ var commentPos = content.indexOf('/*');
+ var bodyPos = content.indexOf('i) {') + 4;
+ expect(changes.keys, unorderedEquals([commentPos, bodyPos]));
+ assertEdit(changes[bodyPos].single,
+ description: NullabilityFixDescription.discardIf,
+ fixReasons: {
+ FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
+ });
}
Future<void> test_fix_reason_edge() async {
@@ -423,30 +431,26 @@
}
Future<void> test_fix_reason_remove_question_from_question_dot() async {
- await analyze('_f(int/*!*/ i) => i?.isEven;');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description, NullabilityFixDescription.removeNullAwareness);
- expect(info.fixReasons, isEmpty);
- }
- }
+ var content = '_f(int/*!*/ i) => i?.isEven;';
+ await analyze(content);
+ var commentPos = content.indexOf('/*');
+ var questionDotPos = content.indexOf('?.');
+ expect(changes.keys, unorderedEquals([commentPos, questionDotPos]));
+ assertEdit(changes[questionDotPos].single,
+ description: NullabilityFixDescription.removeNullAwareness,
+ fixReasons: isEmpty);
}
Future<void>
test_fix_reason_remove_question_from_question_dot_method() async {
- await analyze('_f(int/*!*/ i) => i?.abs();');
- expect(changes, isNotEmpty);
- for (var change in changes.values) {
- expect(change, isNotEmpty);
- for (var edit in change) {
- var info = edit.info;
- expect(info.description, NullabilityFixDescription.removeNullAwareness);
- expect(info.fixReasons, isEmpty);
- }
- }
+ var content = '_f(int/*!*/ i) => i?.abs();';
+ await analyze(content);
+ var commentPos = content.indexOf('/*');
+ var questionDotPos = content.indexOf('?.');
+ expect(changes.keys, unorderedEquals([commentPos, questionDotPos]));
+ assertEdit(changes[questionDotPos].single,
+ description: NullabilityFixDescription.removeNullAwareness,
+ fixReasons: isEmpty);
}
Future<void> test_fix_reason_remove_unnecessary_cast() async {
diff --git a/pkg/nnbd_migration/test/node_builder_test.dart b/pkg/nnbd_migration/test/node_builder_test.dart
index 082f902..adbd403 100644
--- a/pkg/nnbd_migration/test/node_builder_test.dart
+++ b/pkg/nnbd_migration/test/node_builder_test.dart
@@ -1716,33 +1716,33 @@
Future<void> test_variableDeclaration_late_hint_after_metadata() async {
await analyze('@deprecated /*late*/ int i;');
expect(
- variables.isLateHinted(
+ variables.getLateHint(
testSource, findNode.variableDeclarationList('int i')),
- true);
+ isNotNull);
}
Future<void> test_variableDeclaration_late_hint_multiple_comments() async {
await analyze('/*other*/ /*late*/ int i;');
expect(
- variables.isLateHinted(
+ variables.getLateHint(
testSource, findNode.variableDeclarationList('int i')),
- true);
+ isNotNull);
}
Future<void> test_variableDeclaration_late_hint_simple() async {
await analyze('/*late*/ int i;');
expect(
- variables.isLateHinted(
+ variables.getLateHint(
testSource, findNode.variableDeclarationList('int i')),
- true);
+ isNotNull);
}
Future<void> test_variableDeclaration_late_hint_with_spaces() async {
await analyze('/* late */ int i;');
expect(
- variables.isLateHinted(
+ variables.getLateHint(
testSource, findNode.variableDeclarationList('int i')),
- true);
+ isNotNull);
}
Future<void> test_variableDeclaration_type_simple() async {
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 48605b1..834750e 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -96,6 +96,9 @@
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: true);
+ args.addFlag('tree-shake-write-only-fields',
+ help: 'Enable tree shaking of fields which are only written in AOT mode.',
+ defaultsTo: false);
args.addFlag('protobuf-tree-shaker',
help: 'Enable protobuf tree shaker transformation in AOT mode.',
defaultsTo: false);
@@ -170,6 +173,7 @@
final bool useProtobufTreeShaker = options['protobuf-tree-shaker'];
final bool splitOutputByPackages = options['split-output-by-packages'];
final bool minimalKernel = options['minimal-kernel'];
+ final bool treeShakeWriteOnlyFields = options['tree-shake-write-only-fields'];
final List<String> experimentalFlags = options['enable-experiment'];
final Map<String, String> environmentDefines = {};
@@ -246,7 +250,8 @@
bytecodeOptions: bytecodeOptions,
dropAST: dropAST && !splitOutputByPackages,
useProtobufTreeShaker: useProtobufTreeShaker,
- minimalKernel: minimalKernel);
+ minimalKernel: minimalKernel,
+ treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
errorPrinter.printCompilationMessages();
@@ -322,7 +327,8 @@
BytecodeOptions bytecodeOptions,
bool dropAST: false,
bool useProtobufTreeShaker: false,
- bool minimalKernel: false}) async {
+ bool minimalKernel: false,
+ bool treeShakeWriteOnlyFields: false}) async {
// Replace error handler to detect if there are compilation errors.
final errorDetector =
new ErrorDetector(previousErrorHandler: options.onDiagnostic);
@@ -348,7 +354,8 @@
enableAsserts,
useProtobufTreeShaker,
errorDetector,
- minimalKernel: minimalKernel);
+ minimalKernel: minimalKernel,
+ treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
if (minimalKernel) {
// compiledSources is component.uriToSource.keys.
@@ -424,7 +431,8 @@
bool enableAsserts,
bool useProtobufTreeShaker,
ErrorDetector errorDetector,
- {bool minimalKernel: false}) async {
+ {bool minimalKernel: false,
+ bool treeShakeWriteOnlyFields: false}) async {
if (errorDetector.hasCompilationErrors) return;
final coreTypes = new CoreTypes(component);
@@ -443,7 +451,8 @@
if (useGlobalTypeFlowAnalysis) {
globalTypeFlow.transformComponent(target, coreTypes, component,
- treeShakeSignatures: !minimalKernel);
+ treeShakeSignatures: !minimalKernel,
+ treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
} else {
devirtualization.transformComponent(coreTypes, component);
no_dynamic_invocations_annotator.transformComponent(component);
@@ -458,7 +467,8 @@
component, coreTypes, null);
globalTypeFlow.transformComponent(target, coreTypes, component,
- treeShakeSignatures: !minimalKernel);
+ treeShakeSignatures: !minimalKernel,
+ treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
}
// TODO(35069): avoid recomputing CSA by reading it from the platform files.
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 6d591f5..d9ace1d 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -191,12 +191,17 @@
case CallKind.PropertyGet:
assertx(args.values.length == firstParamIndex);
assertx(args.names.isEmpty);
+ fieldValue.isGetterUsed = true;
return fieldValue.getValue(
typeFlowAnalysis, field.isStatic ? null : args.values[0]);
case CallKind.PropertySet:
+ case CallKind.SetFieldInConstructor:
assertx(args.values.length == firstParamIndex + 1);
assertx(args.names.isEmpty);
+ if (selector.callKind == CallKind.PropertySet) {
+ fieldValue.isSetterUsed = true;
+ }
final Type setterArg = args.values[firstParamIndex];
fieldValue.setValue(
setterArg, typeFlowAnalysis, field.isStatic ? null : args.receiver);
@@ -206,6 +211,7 @@
// Call via field.
// TODO(alexmarkov): support function types and use inferred type
// to get more precise return type.
+ fieldValue.isGetterUsed = true;
final receiver = fieldValue.getValue(
typeFlowAnalysis, field.isStatic ? null : args.values[0]);
if (receiver != const EmptyType()) {
@@ -775,6 +781,12 @@
/// Flag indicating if field initializer was executed.
bool isInitialized = false;
+ /// Flag indicating if field getter was executed.
+ bool isGetterUsed = false;
+
+ /// Flag indicating if field setter was executed.
+ bool isSetterUsed = false;
+
_FieldValue(this.field, this.typeGuardSummary, TypesBuilder typesBuilder)
: staticType = typesBuilder.fromStaticType(field.type, true) {
if (field.initializer == null && _isDefaultValueOfFieldObservable()) {
@@ -1396,6 +1408,26 @@
return false;
}
+ /// Returns true if analysis found that getter corresponding to the given
+ /// [field] could be executed.
+ bool isFieldGetterUsed(Field field) {
+ final fieldValue = _fieldValues[field];
+ if (fieldValue != null) {
+ return fieldValue.isGetterUsed;
+ }
+ return false;
+ }
+
+ /// Returns true if analysis found that setter corresponding to the given
+ /// [field] could be executed.
+ bool isFieldSetterUsed(Field field) {
+ final fieldValue = _fieldValues[field];
+ if (fieldValue != null) {
+ return fieldValue.isSetterUsed;
+ }
+ return false;
+ }
+
bool isClassAllocated(Class c) => hierarchyCache.allocatedClasses.contains(c);
Call callSite(TreeNode node) => summaryCollector.callSites[node];
diff --git a/pkg/vm/lib/transformations/type_flow/calls.dart b/pkg/vm/lib/transformations/type_flow/calls.dart
index 06b20ec..55ff9ef 100644
--- a/pkg/vm/lib/transformations/type_flow/calls.dart
+++ b/pkg/vm/lib/transformations/type_flow/calls.dart
@@ -16,7 +16,8 @@
Method, // x.foo(..) or foo()
PropertyGet, // ... x.foo ...
PropertySet, // x.foo = ...
- FieldInitializer,
+ FieldInitializer, // run initializer of a field
+ SetFieldInConstructor, // foo = ... in initializer list in a constructor
}
/// [Selector] encapsulates the way of calling (at the call site).
@@ -55,6 +56,7 @@
return member.getterType;
case CallKind.PropertySet:
case CallKind.FieldInitializer:
+ case CallKind.SetFieldInConstructor:
return const BottomType();
}
return null;
@@ -72,7 +74,8 @@
case CallKind.PropertySet:
return (member is Field) || ((member is Procedure) && member.isSetter);
case CallKind.FieldInitializer:
- return (member is Field);
+ case CallKind.SetFieldInConstructor:
+ return member is Field;
}
return false;
}
@@ -84,6 +87,7 @@
case CallKind.PropertyGet:
return 'get ';
case CallKind.PropertySet:
+ case CallKind.SetFieldInConstructor:
return 'set ';
case CallKind.FieldInitializer:
return 'init ';
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 46a0e48..a2bf551 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -817,6 +817,7 @@
break;
case CallKind.PropertySet:
+ case CallKind.SetFieldInConstructor:
args.add(new Type.nullableAny());
break;
@@ -2088,8 +2089,11 @@
TypeExpr visitFieldInitializer(FieldInitializer node) {
final value = _visit(node.value);
final args = new Args<TypeExpr>([_receiver, value]);
- _makeCall(node,
- new DirectSelector(node.field, callKind: CallKind.PropertySet), args);
+ _makeCall(
+ node,
+ new DirectSelector(node.field,
+ callKind: CallKind.SetFieldInConstructor),
+ args);
return null;
}
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 3a3d400..b24f12e 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -38,7 +38,9 @@
/// Assumes strong mode and closed world.
Component transformComponent(
Target target, CoreTypes coreTypes, Component component,
- {PragmaAnnotationParser matcher, bool treeShakeSignatures: true}) {
+ {PragmaAnnotationParser matcher,
+ bool treeShakeSignatures: true,
+ bool treeShakeWriteOnlyFields: true}) {
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
final hierarchy = new ClassHierarchy(component, coreTypes,
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
@@ -72,17 +74,20 @@
final transformsStopWatch = new Stopwatch()..start();
final treeShaker = new TreeShaker(component, typeFlowAnalysis,
- treeShakeSignatures: treeShakeSignatures)
- ..transformComponent(component);
+ treeShakeSignatures: treeShakeSignatures,
+ treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
+ treeShaker.transformComponent(component);
- new TFADevirtualization(component, typeFlowAnalysis, hierarchy)
+ new TFADevirtualization(
+ component, typeFlowAnalysis, hierarchy, treeShaker.fieldMorpher)
.visitComponent(component);
final unboxingInfo = new UnboxingInfoManager(typeFlowAnalysis);
_makePartition(component, typeFlowAnalysis, unboxingInfo);
- new AnnotateKernel(component, typeFlowAnalysis, unboxingInfo)
+ new AnnotateKernel(
+ component, typeFlowAnalysis, treeShaker.fieldMorpher, unboxingInfo)
.visitComponent(component);
treeShaker.finalizeSignatures();
@@ -100,9 +105,10 @@
/// Devirtualization based on results of type flow analysis.
class TFADevirtualization extends Devirtualization {
final TypeFlowAnalysis _typeFlowAnalysis;
+ final FieldMorpher fieldMorpher;
- TFADevirtualization(
- Component component, this._typeFlowAnalysis, ClassHierarchy hierarchy)
+ TFADevirtualization(Component component, this._typeFlowAnalysis,
+ ClassHierarchy hierarchy, this.fieldMorpher)
: super(_typeFlowAnalysis.environment.coreTypes, component, hierarchy);
@override
@@ -110,7 +116,8 @@
{bool setter = false}) {
final callSite = _typeFlowAnalysis.callSite(node);
if (callSite != null) {
- final Member singleTarget = callSite.monomorphicTarget;
+ final Member singleTarget = fieldMorpher
+ .getMorphedMember(callSite.monomorphicTarget, isSetter: setter);
if (singleTarget != null) {
return new DirectCallMetadata(
singleTarget, callSite.isNullableReceiver);
@@ -123,6 +130,7 @@
/// Annotates kernel AST with metadata using results of type flow analysis.
class AnnotateKernel extends RecursiveVisitor<Null> {
final TypeFlowAnalysis _typeFlowAnalysis;
+ final FieldMorpher fieldMorpher;
final DirectCallMetadataRepository _directCallMetadataRepository;
final InferredTypeMetadataRepository _inferredTypeMetadata;
final UnreachableNodeMetadataRepository _unreachableNodeMetadata;
@@ -134,8 +142,8 @@
final Class _intClass;
Constant _nullConstant;
- AnnotateKernel(
- Component component, this._typeFlowAnalysis, this._unboxingInfo)
+ AnnotateKernel(Component component, this._typeFlowAnalysis, this.fieldMorpher,
+ this._unboxingInfo)
: _directCallMetadataRepository =
component.metadata[DirectCallMetadataRepository.repositoryTag],
_inferredTypeMetadata = new InferredTypeMetadataRepository(),
@@ -328,7 +336,8 @@
// TODO(alexmarkov): figure out how to pass receiver type.
}
- } else if (!member.isAbstract) {
+ } else if (!member.isAbstract &&
+ !fieldMorpher.isExtraMemberWithReachableBody(member)) {
_setUnreachable(member);
} else if (member is! Field) {
final unboxingInfoMetadata =
@@ -357,14 +366,15 @@
// interface target, and table dispatch calls need selector IDs for all
// interface targets.
if (member.isInstanceMember) {
+ final original = fieldMorpher.getOriginalMember(member);
final attrs = new ProcedureAttributesMetadata(
methodOrSetterCalledDynamically:
- _typeFlowAnalysis.isCalledDynamically(member),
+ _typeFlowAnalysis.isCalledDynamically(original),
getterCalledDynamically:
- _typeFlowAnalysis.isGetterCalledDynamically(member),
- hasThisUses: _typeFlowAnalysis.isCalledViaThis(member),
- hasNonThisUses: _typeFlowAnalysis.isCalledNotViaThis(member),
- hasTearOffUses: _typeFlowAnalysis.isTearOffTaken(member),
+ _typeFlowAnalysis.isGetterCalledDynamically(original),
+ hasThisUses: _typeFlowAnalysis.isCalledViaThis(original),
+ hasNonThisUses: _typeFlowAnalysis.isCalledNotViaThis(original),
+ hasTearOffUses: _typeFlowAnalysis.isTearOffTaken(original),
methodOrSetterSelectorId:
_tableSelectorAssigner.methodOrSetterSelectorId(member),
getterSelectorId: _tableSelectorAssigner.getterSelectorId(member));
@@ -558,11 +568,13 @@
///
class TreeShaker {
final TypeFlowAnalysis typeFlowAnalysis;
+ final bool treeShakeWriteOnlyFields;
final Set<Class> _usedClasses = new Set<Class>();
final Set<Class> _classesUsedInType = new Set<Class>();
final Set<Member> _usedMembers = new Set<Member>();
final Set<Extension> _usedExtensions = new Set<Extension>();
final Set<Typedef> _usedTypedefs = new Set<Typedef>();
+ FieldMorpher fieldMorpher;
_TreeShakerTypeVisitor typeVisitor;
_TreeShakerConstantVisitor constantVisitor;
_TreeShakerPass1 _pass1;
@@ -570,7 +582,8 @@
_SignatureShaker _signatureShaker;
TreeShaker(Component component, this.typeFlowAnalysis,
- {bool treeShakeSignatures: true}) {
+ {bool treeShakeSignatures: true, this.treeShakeWriteOnlyFields: true}) {
+ fieldMorpher = new FieldMorpher(this);
typeVisitor = new _TreeShakerTypeVisitor(this);
constantVisitor = new _TreeShakerConstantVisitor(this, typeVisitor);
_pass1 = new _TreeShakerPass1(this);
@@ -595,13 +608,28 @@
bool isClassAllocated(Class c) => typeFlowAnalysis.isClassAllocated(c);
bool isMemberUsed(Member m) => _usedMembers.contains(m);
bool isExtensionUsed(Extension e) => _usedExtensions.contains(e);
- bool isMemberBodyReachable(Member m) => typeFlowAnalysis.isMemberUsed(m);
+ bool isMemberBodyReachable(Member m) =>
+ typeFlowAnalysis.isMemberUsed(m) ||
+ fieldMorpher.isExtraMemberWithReachableBody(m);
bool isFieldInitializerReachable(Field f) =>
typeFlowAnalysis.isFieldInitializerUsed(f);
+ bool isFieldGetterReachable(Field f) => typeFlowAnalysis.isFieldGetterUsed(f);
+ bool isFieldSetterReachable(Field f) => typeFlowAnalysis.isFieldSetterUsed(f);
bool isMemberReferencedFromNativeCode(Member m) =>
typeFlowAnalysis.nativeCodeOracle.isMemberReferencedFromNativeCode(m);
bool isTypedefUsed(Typedef t) => _usedTypedefs.contains(t);
+ bool retainField(Field f) =>
+ isMemberBodyReachable(f) &&
+ (!treeShakeWriteOnlyFields ||
+ isFieldGetterReachable(f) ||
+ (!f.isStatic &&
+ f.initializer != null &&
+ isFieldInitializerReachable(f) &&
+ mayHaveSideEffects(f.initializer)) ||
+ (f.isLate && f.isFinal)) ||
+ isMemberReferencedFromNativeCode(f);
+
void addClassUsedInType(Class c) {
if (_classesUsedInType.add(c)) {
if (kPrintDebug) {
@@ -637,9 +665,15 @@
} else if (m is Procedure) {
func = m.function;
if (m.forwardingStubSuperTarget != null) {
+ m.forwardingStubSuperTarget = fieldMorpher.adjustInstanceCallTarget(
+ m.forwardingStubSuperTarget,
+ isSetter: m.isSetter);
addUsedMember(m.forwardingStubSuperTarget);
}
if (m.forwardingStubInterfaceTarget != null) {
+ m.forwardingStubInterfaceTarget = fieldMorpher
+ .adjustInstanceCallTarget(m.forwardingStubInterfaceTarget,
+ isSetter: m.isSetter);
addUsedMember(m.forwardingStubInterfaceTarget);
}
} else if (m is Constructor) {
@@ -692,6 +726,86 @@
}
}
+class FieldMorpher {
+ final TreeShaker shaker;
+ final Set<Member> _extraMembersWithReachableBody = <Member>{};
+ final Map<Field, Member> _gettersForRemovedFields = <Field, Member>{};
+ final Map<Field, Member> _settersForRemovedFields = <Field, Member>{};
+ final Map<Member, Field> _removedFields = <Member, Field>{};
+
+ FieldMorpher(this.shaker);
+
+ Member _createAccessorForRemovedField(Field field, bool isSetter) {
+ assertx(!field.isStatic);
+ assertx(!shaker.retainField(field));
+ Procedure accessor;
+ if (isSetter) {
+ final isAbstract = !shaker.isFieldSetterReachable(field);
+ final parameter = new VariableDeclaration('value', type: field.type)
+ ..isCovariant = field.isCovariant
+ ..isGenericCovariantImpl = field.isGenericCovariantImpl
+ ..fileOffset = field.fileOffset;
+ accessor = new Procedure(
+ field.name,
+ ProcedureKind.Setter,
+ new FunctionNode(null,
+ positionalParameters: [parameter], returnType: const VoidType())
+ ..fileOffset = field.fileOffset,
+ isAbstract: isAbstract,
+ fileUri: field.fileUri);
+ if (!isAbstract) {
+ _extraMembersWithReachableBody.add(accessor);
+ }
+ } else {
+ accessor = new Procedure(field.name, ProcedureKind.Getter,
+ new FunctionNode(null, returnType: field.type),
+ isAbstract: true, fileUri: field.fileUri);
+ }
+ accessor.fileOffset = field.fileOffset;
+ field.enclosingClass.addMember(accessor);
+ _removedFields[accessor] = field;
+ shaker.addUsedMember(accessor);
+ return accessor;
+ }
+
+ /// Return a replacement for an instance call target.
+ /// If necessary, creates a getter or setter as a replacement if target is a
+ /// field which is going to be removed by the tree shaker.
+ /// This method is used during tree shaker pass 1.
+ Member adjustInstanceCallTarget(Member target, {bool isSetter = false}) {
+ if (target is Field && !shaker.retainField(target)) {
+ final targets =
+ isSetter ? _settersForRemovedFields : _gettersForRemovedFields;
+ return targets[target] ??=
+ _createAccessorForRemovedField(target, isSetter);
+ }
+ return target;
+ }
+
+ bool isExtraMemberWithReachableBody(Member member) =>
+ _extraMembersWithReachableBody.contains(member);
+
+ /// Return a member which replaced [target] in instance calls.
+ /// This method can be used after tree shaking to discover replacement.
+ Member getMorphedMember(Member target, {bool isSetter = false}) {
+ if (target == null) {
+ return null;
+ }
+ final targets =
+ isSetter ? _settersForRemovedFields : _gettersForRemovedFields;
+ return targets[target] ?? target;
+ }
+
+ /// Return original member which was replaced by [target] in instance calls.
+ /// This method can be used after tree shaking.
+ Member getOriginalMember(Member target) {
+ if (target == null) {
+ return null;
+ }
+ return _removedFields[target] ?? target;
+ }
+}
+
/// Visits Dart types and collects all classes and typedefs used in types.
/// This visitor is used during pass 1 of tree shaking. It is a separate
/// visitor because [Transformer] does not provide a way to traverse types.
@@ -742,6 +856,7 @@
/// transforms unreachable calls into 'throw' expressions.
class _TreeShakerPass1 extends Transformer {
final TreeShaker shaker;
+ final FieldMorpher fieldMorpher;
final TypeEnvironment environment;
final List<Initializer> additionalInitializers = [];
Procedure _unsafeCast;
@@ -759,7 +874,8 @@
}
_TreeShakerPass1(this.shaker)
- : environment = shaker.typeFlowAnalysis.environment;
+ : fieldMorpher = shaker.fieldMorpher,
+ environment = shaker.typeFlowAnalysis.environment;
void transform(Component component) {
component.transformChildren(this);
@@ -802,7 +918,9 @@
'Attempt to execute code removed by Dart AOT compiler (TFA)'));
}
for (var arg in args.reversed) {
- node = new Let(new VariableDeclaration(null, initializer: arg), node);
+ if (mayHaveSideEffects(arg)) {
+ node = new Let(new VariableDeclaration(null, initializer: arg), node);
+ }
}
Statistics.callsDropped++;
return node;
@@ -894,7 +1012,7 @@
@override
TreeNode visitField(Field node) {
- if (shaker.isMemberBodyReachable(node)) {
+ if (shaker.retainField(node)) {
if (kPrintTrace) {
tracePrint("Visiting $node");
}
@@ -906,12 +1024,10 @@
node.initializer = _makeUnreachableCall([])..parent = node;
}
}
- } else if (shaker.isMemberReferencedFromNativeCode(node)) {
- // Preserve members referenced from native code to satisfy lookups, even
- // if they are not reachable. An instance member could be added via
- // native code entry point but still unreachable if no instances of
- // its enclosing class are allocated.
- shaker.addUsedMember(node);
+ } else if (shaker.isFieldSetterReachable(node) && !node.isStatic) {
+ // Make sure setter is created to replace the field even if field is not
+ // used as an instance call target.
+ fieldMorpher.adjustInstanceCallTarget(node, isSetter: true);
}
return node;
}
@@ -923,6 +1039,8 @@
return _makeUnreachableCall(
_flattenArguments(node.arguments, receiver: node.receiver));
} else {
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
@@ -936,6 +1054,8 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver]);
} else {
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
@@ -949,6 +1069,8 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver, node.value]);
} else {
+ node.interfaceTarget = fieldMorpher
+ .adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
@@ -962,6 +1084,8 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall(_flattenArguments(node.arguments));
} else {
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
@@ -975,6 +1099,8 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall([]);
} else {
+ node.interfaceTarget =
+ fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
@@ -988,6 +1114,8 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.value]);
} else {
+ node.interfaceTarget = fieldMorpher
+ .adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
@@ -1117,7 +1245,11 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.value]);
} else {
- assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
+ final target = node.target;
+ assertx(shaker.isMemberBodyReachable(target), details: node);
+ if (target is Field && !shaker.retainField(target)) {
+ return node.value;
+ }
return node;
}
}
@@ -1129,7 +1261,7 @@
return _makeUnreachableCall(
_flattenArguments(node.arguments, receiver: node.receiver));
} else {
- assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
+ assertx(shaker.isMemberBodyReachable(node.target), details: node);
return node;
}
}
@@ -1140,7 +1272,10 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver]);
} else {
- assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
+ final target = node.target;
+ assertx(shaker.isMemberBodyReachable(target), details: node);
+ assertx(target is! Field || shaker.isFieldGetterReachable(target),
+ details: node);
return node;
}
}
@@ -1151,7 +1286,9 @@
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver, node.value]);
} else {
- assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
+ assertx(shaker.isMemberBodyReachable(node.target), details: node);
+ node.target =
+ fieldMorpher.adjustInstanceCallTarget(node.target, isSetter: true);
return node;
}
}
@@ -1203,12 +1340,20 @@
}
@override
- visitFieldInitializer(FieldInitializer node) {
+ TreeNode visitFieldInitializer(FieldInitializer node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableInitializer([node.value]);
} else {
assertx(shaker.isMemberBodyReachable(node.field), details: node.field);
+ if (!shaker.retainField(node.field)) {
+ if (mayHaveSideEffects(node.value)) {
+ return LocalInitializer(
+ VariableDeclaration(null, initializer: node.value));
+ } else {
+ return null;
+ }
+ }
return node;
}
}
@@ -1587,3 +1732,26 @@
constant.type.accept(typeVisitor);
}
}
+
+bool mayHaveSideEffects(Expression node) {
+ if (node is BasicLiteral ||
+ node is ConstantExpression ||
+ node is ThisExpression) {
+ return false;
+ }
+ if (node is VariableGet && !node.variable.isLate) {
+ return false;
+ }
+ if (node is StaticGet) {
+ final target = node.target;
+ if (target is Field && !target.isLate) {
+ final initializer = target.initializer;
+ if (initializer == null ||
+ initializer is BasicLiteral ||
+ initializer is ConstantExpression) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
index 9ae4ab1..e6599fe 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
@@ -15,7 +15,7 @@
[@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasTearOffUses:false,methodOrSetterSelectorId:1] [@vm.unboxing-info.metadata=(b)->d] operator []([@vm.inferred-type.metadata=!] core::int* i) → core::double*
return [@vm.direct-call.metadata=_Float64List::[]] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.direct-call.metadata=_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}.{core::List::[]}([@vm.direct-call.metadata=_IntegerImplementation::+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}([@vm.direct-call.metadata=_Vector::_offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset}));
[@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2] operator []=([@vm.inferred-type.metadata=dart.core::_OneByteString] core::int* i, core::double* value) → void {
- let dynamic #t1 = [@vm.direct-call.metadata=_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = i in let dynamic #t3 = [@vm.direct-call.metadata=_Vector::_offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+ let dynamic #t1 = [@vm.direct-call.metadata=_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = [@vm.direct-call.metadata=_Vector::_offset] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
}
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] [@vm.unboxing-info.metadata=(b)->d] operator *([@vm.inferred-type.metadata=#lib::_Vector?] self::_Vector* a) → core::double* {
core::double* result = 0.0;
@@ -27,7 +27,7 @@
[@vm.inferred-type.metadata=#lib::_Vector?]static field self::_Vector* v = new self::_Vector::•(10);
[@vm.inferred-type.metadata=dart.core::_Double?]static field core::double* x = 0.0;
static method main(core::List<core::String*>* args) → dynamic {
- core::Stopwatch* timer = let final core::Stopwatch* #t4 = new core::Stopwatch::•() in let final void #t5 = [@vm.direct-call.metadata=Stopwatch::start] [@vm.inferred-type.metadata=!? (skip check)] #t4.{core::Stopwatch::start}() in #t4;
+ core::Stopwatch* timer = let final core::Stopwatch* #t3 = new core::Stopwatch::•() in let final void #t4 = [@vm.direct-call.metadata=Stopwatch::start] [@vm.inferred-type.metadata=!? (skip check)] #t3.{core::Stopwatch::start}() in #t3;
for (core::int* i = 0; [@vm.direct-call.metadata=_IntegerImplementation::<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100000000); i = [@vm.direct-call.metadata=_IntegerImplementation::+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1)) {
self::x = [@vm.direct-call.metadata=_Double::+??] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.inferred-type.metadata=dart.core::_Double?] self::x.{core::double::+}([@vm.direct-call.metadata=_Vector::*??] [@vm.inferred-type.metadata=dart.core::_Double (skip check)] [@vm.inferred-type.metadata=#lib::_Vector?] self::v.{self::_Vector::*}([@vm.inferred-type.metadata=#lib::_Vector?] self::v));
}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart b/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart
index f2d7d4d..b60f24c5 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart
@@ -8,8 +8,6 @@
// @dart = 2.9
-import "package:expect/expect.dart";
-
class _SplayTreeNode<Node extends _SplayTreeNode<Node>> {
Node? left;
_SplayTreeNode();
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart.expect
index 0285a9d..3128139 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_41452_nnbd_strong.dart.expect
@@ -2,8 +2,6 @@
import self as self;
import "dart:core" as core;
-import "package:expect/expect.dart";
-
abstract class _SplayTreeNode<Node extends self::_SplayTreeNode<self::_SplayTreeNode::Node> = self::_SplayTreeNode<dynamic>> extends core::Object {
[@vm.inferred-type.metadata=dart.core::Null? (value: null)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] generic-covariant-impl field self::_SplayTreeNode::Node? left = null;
constructor •() → self::_SplayTreeNode<self::_SplayTreeNode::Node>
@@ -16,7 +14,6 @@
;
}
abstract class _SplayTree<Node extends self::_SplayTreeNode<self::_SplayTree::Node> = self::_SplayTreeNode<dynamic>> extends core::Object {
-[@vm.inferred-type.metadata=dart.core::Null? (value: null)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] generic-covariant-impl field self::_SplayTree::Node? _root = null;
synthetic constructor •() → self::_SplayTree<self::_SplayTree::Node>
: super core::Object::•()
;
@@ -26,9 +23,10 @@
return;
core::print([@vm.direct-call.metadata=_SplayTreeNode::left] [@vm.inferred-type.metadata=dart.core::Null? (value: null)] root{self::_SplayTree::Node}.{self::_SplayTreeNode::left});
}
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:5] abstract get /*isNullableByDefault*/ _root() → self::_SplayTree::Node?;
}
class SplayTreeMap<V extends core::Object? = dynamic> extends self::_SplayTree<self::_SplayTreeMapNode<self::SplayTreeMap::V%>> {
-[@vm.inferred-type.metadata=#lib::_SplayTreeMapNode] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] generic-covariant-impl field self::_SplayTreeMapNode<self::SplayTreeMap::V%>? _root = new self::_SplayTreeMapNode::•<self::SplayTreeMap::V%>();
+[@vm.inferred-type.metadata=#lib::_SplayTreeMapNode] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:6,getterSelectorId:5] generic-covariant-impl field self::_SplayTreeMapNode<self::SplayTreeMap::V%>? _root = new self::_SplayTreeMapNode::•<self::SplayTreeMap::V%>();
synthetic constructor •() → self::SplayTreeMap<self::SplayTreeMap::V%>
: super self::_SplayTree::•()
;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/unreachable.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/unreachable.dart.expect
index 47a15c4..0ed46d3 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/unreachable.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/unreachable.dart.expect
@@ -14,7 +14,7 @@
[@vm.inferred-type.metadata=#lib::B?]static field self::I* ii = new self::B::•();
static method bar([@vm.inferred-type.metadata=#lib::B?] self::I* i) → void {
if(i is self::A*) {
- let dynamic #t1 = i{self::A*} in let dynamic #t2 = 42 in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
+ throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
}
}
static method main(core::List<core::String*>* args) → dynamic {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect
index 079f6e9..3ba876c 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect
@@ -2,8 +2,6 @@
import self as self;
import "dart:core" as core;
-abstract class A extends core::Object {
-}
class B extends core::Object {
constructor •() → self::B*
: super core::Object::•() {
@@ -16,8 +14,7 @@
: super core::Object::•()
;
}
-[@vm.inferred-type.metadata=dart.core::Null? (value: null)]static field self::A* field = throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
static method main() → void {
- self::field = null;
+ null;
[@vm.direct-call.metadata=C::instanceField] [@vm.inferred-type.metadata=!? (skip check)] new self::C::•().{self::C::instanceField} = null;
}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart
new file mode 100644
index 0000000..af6a9b8
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart
@@ -0,0 +1,91 @@
+// 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.
+
+// Test for tree shaking of write-only fields.
+
+import "package:expect/expect.dart";
+
+foo() {}
+
+class A {
+ // Should be removed.
+ var unused1;
+
+ // Should be removed.
+ var unused2 = 42;
+
+ // Not removed due to a non-trivial initializer.
+ var unused3 = foo();
+}
+
+class B {
+ // Should be removed.
+ var unused4;
+
+ // Should be removed.
+ var unused5;
+
+ B(this.unused4) : unused5 = foo();
+}
+
+class C<T> {
+ // Should be replaced with setter.
+ T bar;
+}
+
+class D implements C<int> {
+ // Should be replaced with setter.
+ int bar;
+}
+
+class E {
+ // Should be replaced with getter.
+ final int bar;
+
+ E(this.bar);
+}
+
+class F implements E {
+ int get bar => 42;
+}
+
+class G {
+ // Not removed because used in a constant.
+ final int bazz;
+
+ const G(this.bazz);
+}
+
+class H {
+ // Should be replaced with setter.
+ int unused6;
+}
+
+class I extends H {
+ foo() {
+ super.unused6 = 3;
+ }
+}
+
+// Should be removed.
+int unusedStatic7 = foo();
+
+void main() {
+ new A();
+ new B('hi');
+
+ C<num> c = new D();
+ Expect.throws(() {
+ c.bar = 3.14;
+ });
+
+ E e = new F();
+ Expect.equals(42, e.bar);
+
+ Expect.isTrue(!identical(const G(1), const G(2)));
+
+ new I().foo();
+
+ unusedStatic7 = 5;
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
new file mode 100644
index 0000000..db8220b
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field2.dart.expect
@@ -0,0 +1,68 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+class A extends core::Object {
+[@vm.inferred-type.metadata=dart.core::Null? (value: null)] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] field dynamic unused3 = [@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::foo();
+ synthetic constructor •() → self::A*
+ : super core::Object::•()
+ ;
+}
+class B extends core::Object {
+ constructor •([@vm.inferred-type.metadata=dart.core::_OneByteString (value: hi)] dynamic unused4) → self::B*
+ : dynamic #t1 = [@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::foo(), super core::Object::•()
+ ;
+}
+abstract class C<T extends core::Object* = dynamic> extends core::Object {
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] abstract set bar(generic-covariant-impl self::C::T* value) → void;
+}
+class D extends core::Object implements self::C<core::int*> {
+ synthetic constructor •() → self::D*
+ : super core::Object::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] set bar(generic-covariant-impl core::int* value) → void;
+}
+abstract class E extends core::Object {
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] abstract get bar() → core::int*;
+}
+class F extends core::Object implements self::E {
+ synthetic constructor •() → self::F*
+ : super core::Object::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:4] get bar() → core::int*
+ return 42;
+}
+class G extends core::Object /*hasConstConstructor*/ {
+[@vm.inferred-type.metadata=dart.core::_Smi] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:5,getterSelectorId:6] final field core::int* bazz;
+}
+abstract class H extends core::Object {
+ synthetic constructor •() → self::H*
+ : super core::Object::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:7] set unused6(core::int* value) → void;
+}
+class I extends self::H {
+ synthetic constructor •() → self::I*
+ : super self::H::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:8,getterSelectorId:9] method foo() → dynamic {
+ super.{self::H::unused6} = 3;
+ }
+}
+static method foo() → dynamic {}
+static method main() → void {
+ new self::A::•();
+ new self::B::•("hi");
+ self::C<core::num*>* c = new self::D::•();
+ exp::Expect::throws<dynamic>(() → core::Null? {
+ [@vm.call-site-attributes.metadata=receiverType:InterfaceType(C<num*>*)] [@vm.direct-call.metadata=D::bar] c.{self::C::bar} = 3.14;
+ });
+ self::E* e = new self::F::•();
+ exp::Expect::equals(42, [@vm.direct-call.metadata=F::bar] [@vm.inferred-type.metadata=dart.core::_Smi (value: 42)] e.{self::E::bar});
+ exp::Expect::isTrue(![@vm.inferred-type.metadata=dart.core::bool] core::identical(#C2, #C4));
+ [@vm.direct-call.metadata=I::foo] [@vm.inferred-type.metadata=!? (skip check)] new self::I::•().{self::I::foo}();
+ 5;
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field3_nnbd.dart b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field3_nnbd.dart
new file mode 100644
index 0000000..bcc6ec4
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field3_nnbd.dart
@@ -0,0 +1,42 @@
+// 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.
+
+// Test for tree shaking of write-only late fields.
+// This test requires non-nullable experiment.
+
+// @dart = 2.9
+
+foo() {}
+
+class A {
+ // Should be replaced with setter.
+ late int x;
+
+ use() {
+ x = 3;
+ }
+}
+
+class B {
+ // Should be retained.
+ late final int x;
+
+ use() {
+ x = 3;
+ }
+}
+
+// Should be removed.
+late int staticLateA;
+
+// Should be retained.
+late final int staticLateB;
+
+void main() {
+ new A().use();
+ new B().use();
+
+ staticLateA = 4;
+ staticLateB = 4;
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field3_nnbd.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field3_nnbd.dart.expect
new file mode 100644
index 0000000..fd4556d
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field3_nnbd.dart.expect
@@ -0,0 +1,29 @@
+library #lib /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method use() → dynamic {
+ [@vm.direct-call.metadata=A::x] [@vm.inferred-type.metadata=!? (skip check)] this.{self::A::x} = 3;
+ }
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] set /*isNullableByDefault*/ x(core::int value) → void;
+}
+class B extends core::Object {
+[@vm.inferred-type.metadata=dart.core::_Smi?] [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] late final [setter] field core::int x;
+ synthetic constructor •() → self::B
+ : super core::Object::•()
+ ;
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] method use() → dynamic {
+ [@vm.direct-call.metadata=B::x] [@vm.inferred-type.metadata=!? (skip check)] this.{self::B::x} = 3;
+ }
+}
+[@vm.inferred-type.metadata=dart.core::_Smi?]late static final field core::int staticLateB;
+static method main() → void {
+ [@vm.direct-call.metadata=A::use] [@vm.inferred-type.metadata=!? (skip check)] new self::A::•().{self::A::use}();
+ [@vm.direct-call.metadata=B::use] [@vm.inferred-type.metadata=!? (skip check)] new self::B::•().{self::B::use}();
+ 4;
+ self::staticLateB = 4;
+}
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index 88b27a9..c7d66dc 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -236,6 +236,7 @@
"vm:libdart_lib",
"vm:libdart_vm",
]
+ compiler_lib = "vm:libdart_compiler"
extra_configs = [ ":dart_shared_lib" ]
include_dirs = [ "." ]
public_configs = [ ":dart_public_config" ]
diff --git a/runtime/configs.gni b/runtime/configs.gni
index 04d8998..971d242 100644
--- a/runtime/configs.gni
+++ b/runtime/configs.gni
@@ -65,56 +65,67 @@
suffix = "_jit"
configs = _jit_config
snapshot = true
+ compiler = true
},
{
suffix = "_jit_product"
configs = _jit_product_config
snapshot = true
+ compiler = true
},
{
suffix = "_precompiled_runtime"
configs = _precompiled_runtime_config
snapshot = true
+ compiler = false
},
{
suffix = "_precompiled_runtime_product"
configs = _precompiled_runtime_product_config
snapshot = true
+ compiler = false
},
{
suffix = "_precompiler"
configs = _precompiler_config
snapshot = false
+ compiler = true
},
{
suffix = "_precompiler_product"
configs = _precompiler_product_config
snapshot = false
+ compiler = true
},
{
suffix = "_precompiler_fuchsia"
configs = _precompiler_fuchsia_config
snapshot = false
+ compiler = true
},
{
suffix = "_precompiler_product_fuchsia"
configs = _precompiler_product_fuchsia_config
snapshot = false
+ compiler = true
},
{
suffix = "_precompiler_host_targeting_host"
configs = _precompiler_host_targeting_host_config
snapshot = false
+ compiler = true
},
{
suffix = "_precompiler_product_host_targeting_host"
configs = _precompiler_product_host_targeting_host_config
snapshot = false
+ compiler = true
},
{
suffix = "_libfuzzer"
configs = _libfuzzer_config
snapshot = true
+ compiler = true
},
]
@@ -177,6 +188,13 @@
foreach(dep, configurable_deps) {
configured_deps += [ "${dep}${conf.suffix}" ]
}
+ if (defined(compiler_lib)) {
+ if (conf.compiler) {
+ configured_deps += [ "${compiler_lib}${conf.suffix}" ]
+ } else {
+ not_needed([ "compiler_lib" ])
+ }
+ }
deps = configured_deps + extra_deps
if (conf.snapshot) {
if (defined(snapshot_sources)) {
@@ -190,3 +208,47 @@
}
}
}
+
+template("library_for_all_configs_with_compiler") {
+ assert(defined(invoker.target_type))
+ extra_configs = []
+ if (defined(invoker.extra_configs)) {
+ extra_configs += invoker.extra_configs
+ }
+ configurable_deps = []
+ if (defined(invoker.configurable_deps)) {
+ configurable_deps += invoker.configurable_deps
+ }
+ extra_deps = []
+ if (defined(invoker.extra_deps)) {
+ extra_deps += invoker.extra_deps
+ }
+ foreach(conf, _all_configs) {
+ if (conf.compiler) {
+ target(invoker.target_type, "${target_name}${conf.suffix}") {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "extra_configs",
+ "extra_deps",
+ "configurable_deps",
+ ])
+ configs += conf.configs + extra_configs
+ configured_deps = []
+ foreach(dep, configurable_deps) {
+ configured_deps += [ "${dep}${conf.suffix}" ]
+ }
+ deps = configured_deps + extra_deps
+ if (conf.snapshot) {
+ if (defined(snapshot_sources)) {
+ sources += snapshot_sources
+ }
+ } else {
+ if (defined(snapshot_sources)) {
+ not_needed([ "snapshot_sources" ])
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc
index 47ead67..58f1d77 100644
--- a/runtime/lib/ffi.cc
+++ b/runtime/lib/ffi.cc
@@ -8,11 +8,7 @@
#include "vm/bootstrap_natives.h"
#include "vm/class_finalizer.h"
#include "vm/class_id.h"
-#include "vm/compiler/assembler/assembler.h"
-#include "vm/compiler/ffi/call.h"
-#include "vm/compiler/ffi/callback.h"
#include "vm/compiler/ffi/native_type.h"
-#include "vm/compiler/jit/compiler.h"
#include "vm/exceptions.h"
#include "vm/flags.h"
#include "vm/log.h"
@@ -22,6 +18,13 @@
#include "vm/object_store.h"
#include "vm/symbols.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/assembler/assembler.h"
+#include "vm/compiler/ffi/call.h"
+#include "vm/compiler/ffi/callback.h"
+#include "vm/compiler/jit/compiler.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
// The following functions are runtime checks on type arguments.
@@ -69,7 +72,7 @@
return Double::Cast(instance);
}
-// Calcuate the size of a native type.
+// Calculate the size of a native type.
//
// You must check [IsConcreteNativeType] and [CheckSized] first to verify that
// this type has a defined size.
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 465c84f..1855954 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -167,7 +167,11 @@
return;
}
+#if defined(DART_PRECOMPILED_RUNTIME)
+ isolate = CreateWithinExistingIsolateGroupAOT(group, name, &error);
+#else
isolate = CreateWithinExistingIsolateGroup(group, name, &error);
+#endif
parent_isolate_->DecrementSpawnCount();
parent_isolate_ = nullptr;
if (isolate == nullptr) {
diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc
index eb4237c..6deab66 100644
--- a/runtime/lib/regexp.cc
+++ b/runtime/lib/regexp.cc
@@ -8,10 +8,13 @@
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/regexp_assembler_bytecode.h"
-#include "vm/regexp_assembler_ir.h"
#include "vm/regexp_parser.h"
#include "vm/thread.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/regexp_assembler_ir.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
DEFINE_NATIVE_ENTRY(RegExp_factory, 0, 6) {
diff --git a/runtime/observatory/tests/service/contexts_test.dart b/runtime/observatory/tests/service/contexts_test.dart
index ad17f38..33021a1 100644
--- a/runtime/observatory/tests/service/contexts_test.dart
+++ b/runtime/observatory/tests/service/contexts_test.dart
@@ -8,7 +8,15 @@
import 'package:test/test.dart';
import 'test_helper.dart';
-var cleanBlock, copyingBlock, fullBlock, fullBlockWithChain;
+// Make sure these variables are not removed by the tree shaker.
+@pragma("vm:entry-point")
+var cleanBlock;
+@pragma("vm:entry-point")
+var copyingBlock;
+@pragma("vm:entry-point")
+var fullBlock;
+@pragma("vm:entry-point")
+var fullBlockWithChain;
Function genCleanBlock() {
block(x) => x;
diff --git a/runtime/observatory/tests/service/dominator_tree_vm_test.dart b/runtime/observatory/tests/service/dominator_tree_vm_test.dart
index ebb9c77..3fb617b 100644
--- a/runtime/observatory/tests/service/dominator_tree_vm_test.dart
+++ b/runtime/observatory/tests/service/dominator_tree_vm_test.dart
@@ -12,62 +12,86 @@
// small example from [Lenguaer & Tarjan 1979]
class R {
+ // All fields are marked with @pragma("vm:entry-point")
+ // in order to make sure they are not removed by the tree shaker
+ // even though they are never read.
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
+ @pragma("vm:entry-point")
var z;
}
class A {
+ @pragma("vm:entry-point")
var x;
}
class B {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
+ @pragma("vm:entry-point")
var z;
}
class C {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class D {
+ @pragma("vm:entry-point")
var x;
}
class E {
+ @pragma("vm:entry-point")
var x;
}
class F {
+ @pragma("vm:entry-point")
var x;
}
class G {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class H {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class I {
+ @pragma("vm:entry-point")
var x;
}
class J {
+ @pragma("vm:entry-point")
var x;
}
class K {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class L {
+ @pragma("vm:entry-point")
var x;
}
diff --git a/runtime/observatory/tests/service/dominator_tree_vm_with_double_field_test.dart b/runtime/observatory/tests/service/dominator_tree_vm_with_double_field_test.dart
index 858f10a..eee07b2 100644
--- a/runtime/observatory/tests/service/dominator_tree_vm_with_double_field_test.dart
+++ b/runtime/observatory/tests/service/dominator_tree_vm_with_double_field_test.dart
@@ -22,62 +22,86 @@
// small example from [Lenguaer & Tarjan 1979]
class R {
final double fld = getDoubleWithHeapObjectTag();
+ // Fields are marked with @pragma("vm:entry-point")
+ // in order to make sure they are not removed by the tree shaker
+ // even though they are never read.
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
+ @pragma("vm:entry-point")
var z;
}
class A {
+ @pragma("vm:entry-point")
var x;
}
class B {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
+ @pragma("vm:entry-point")
var z;
}
class C {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class D {
+ @pragma("vm:entry-point")
var x;
}
class E {
+ @pragma("vm:entry-point")
var x;
}
class F {
+ @pragma("vm:entry-point")
var x;
}
class G {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class H {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class I {
+ @pragma("vm:entry-point")
var x;
}
class J {
+ @pragma("vm:entry-point")
var x;
}
class K {
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
class L {
+ @pragma("vm:entry-point")
var x;
}
diff --git a/runtime/observatory/tests/service/get_instances_rpc_test.dart b/runtime/observatory/tests/service/get_instances_rpc_test.dart
index 0f234be..696f521 100644
--- a/runtime/observatory/tests/service/get_instances_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_instances_rpc_test.dart
@@ -9,7 +9,10 @@
class _TestClass {
_TestClass(this.x, this.y);
+ // Make sure these fields are not removed by the tree shaker.
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
diff --git a/runtime/observatory/tests/service/get_retained_size_rpc_test.dart b/runtime/observatory/tests/service/get_retained_size_rpc_test.dart
index 7060f13..3241ce5 100644
--- a/runtime/observatory/tests/service/get_retained_size_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_retained_size_rpc_test.dart
@@ -9,10 +9,14 @@
class _TestClass {
_TestClass(this.x, this.y);
+ // Make sure these fields are not removed by the tree shaker.
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
+@pragma("vm:entry-point")
var myVar;
@pragma("vm:entry-point")
diff --git a/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart b/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
index 78cacc0..1a036e5 100644
--- a/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
@@ -9,7 +9,10 @@
class _TestClass {
_TestClass();
+ // Make sure these fields are not removed by the tree shaker.
+ @pragma("vm:entry-point")
var x;
+ @pragma("vm:entry-point")
var y;
}
@@ -193,7 +196,7 @@
'limit': 100,
};
var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
- expect(result['gcRootType'], 'object store');
+ expect(result['gcRootType'], 'isolate_object store');
expect(result['elements'].length, 0);
},
];
diff --git a/runtime/observatory/tests/service/inbound_references_test.dart b/runtime/observatory/tests/service/inbound_references_test.dart
index 4c1e496..267f1b1 100644
--- a/runtime/observatory/tests/service/inbound_references_test.dart
+++ b/runtime/observatory/tests/service/inbound_references_test.dart
@@ -9,6 +9,8 @@
import 'test_helper.dart';
class Node {
+ // Make sure this field is not removed by the tree shaker.
+ @pragma("vm:entry-point")
var edge;
}
diff --git a/runtime/observatory/tests/service/instance_field_order_rpc_test.dart b/runtime/observatory/tests/service/instance_field_order_rpc_test.dart
index 9ff16e4..b1a3c38 100644
--- a/runtime/observatory/tests/service/instance_field_order_rpc_test.dart
+++ b/runtime/observatory/tests/service/instance_field_order_rpc_test.dart
@@ -9,12 +9,17 @@
import 'test_helper.dart';
class Super {
+ // Make sure these fields are not removed by the tree shaker.
+ @pragma("vm:entry-point")
var z = 1;
+ @pragma("vm:entry-point")
var y = 2;
}
class Sub extends Super {
+ @pragma("vm:entry-point")
var y = 3;
+ @pragma("vm:entry-point")
var x = 4;
}
diff --git a/runtime/observatory/tests/service/object_graph_vm_test.dart b/runtime/observatory/tests/service/object_graph_vm_test.dart
index 852c823..9414709 100644
--- a/runtime/observatory/tests/service/object_graph_vm_test.dart
+++ b/runtime/observatory/tests/service/object_graph_vm_test.dart
@@ -9,7 +9,10 @@
import 'test_helper.dart';
class Foo {
+ // Make sure these fields are not removed by the tree shaker.
+ @pragma("vm:entry-point")
dynamic left;
+ @pragma("vm:entry-point")
dynamic right;
}
diff --git a/runtime/observatory/tests/service/reachable_size_test.dart b/runtime/observatory/tests/service/reachable_size_test.dart
index 6bc92f5..bca88d8 100644
--- a/runtime/observatory/tests/service/reachable_size_test.dart
+++ b/runtime/observatory/tests/service/reachable_size_test.dart
@@ -9,7 +9,11 @@
import 'service_test_common.dart';
class Pair {
- var x, y;
+ // Make sure these fields are not removed by the tree shaker.
+ @pragma("vm:entry-point")
+ var x;
+ @pragma("vm:entry-point")
+ var y;
}
var p1;
diff --git a/runtime/observatory/tests/service/regexp_function_test.dart b/runtime/observatory/tests/service/regexp_function_test.dart
index a44cb5b..d11abe0 100644
--- a/runtime/observatory/tests/service/regexp_function_test.dart
+++ b/runtime/observatory/tests/service/regexp_function_test.dart
@@ -9,7 +9,10 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+// Make sure these variables are not removed by the tree shaker.
+@pragma("vm:entry-point")
var regex0;
+@pragma("vm:entry-point")
var regex;
void script() {
diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
index 5f260b4..00c7f4f 100644
--- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
+++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
@@ -54,15 +54,21 @@
// Run the AOT compiler with/without Dwarf stack traces.
final scriptDwarfSnapshot = path.join(tempDir, 'dwarf.so');
final scriptNonDwarfSnapshot = path.join(tempDir, 'non_dwarf.so');
+ final scriptDwarfDebugInfo = path.join(tempDir, 'debug_info.so');
await Future.wait(<Future>[
run(genSnapshot, <String>[
- '--dwarf-stack-traces',
+ // We test --dwarf-stack-traces-mode, not --dwarf-stack-traces, because
+ // the latter is a handler that sets the former and also may change
+ // other flags. This way, we limit the difference between the two
+ // snapshots and also directly test the flag saved as a VM global flag.
+ '--dwarf-stack-traces-mode',
+ '--save-debugging-info=$scriptDwarfDebugInfo',
'--snapshot-kind=app-aot-elf',
'--elf=$scriptDwarfSnapshot',
scriptDill,
]),
run(genSnapshot, <String>[
- '--no-dwarf-stack-traces',
+ '--no-dwarf-stack-traces-mode',
'--snapshot-kind=app-aot-elf',
'--elf=$scriptNonDwarfSnapshot',
scriptDill,
@@ -71,24 +77,24 @@
// Run the resulting Dwarf-AOT compiled script.
final dwarfTrace1 = await runError(aotRuntime, <String>[
- '--dwarf-stack-traces',
+ '--dwarf-stack-traces-mode',
scriptDwarfSnapshot,
scriptDill,
]);
final dwarfTrace2 = await runError(aotRuntime, <String>[
- '--no-dwarf-stack-traces',
+ '--no-dwarf-stack-traces-mode',
scriptDwarfSnapshot,
scriptDill,
]);
// Run the resulting non-Dwarf-AOT compiled script.
final nonDwarfTrace1 = await runError(aotRuntime, <String>[
- '--dwarf-stack-traces',
+ '--dwarf-stack-traces-mode',
scriptNonDwarfSnapshot,
scriptDill,
]);
final nonDwarfTrace2 = await runError(aotRuntime, <String>[
- '--no-dwarf-stack-traces',
+ '--no-dwarf-stack-traces-mode',
scriptNonDwarfSnapshot,
scriptDill,
]);
@@ -105,9 +111,7 @@
// Check that translating the DWARF stack trace (without internal frames)
// matches the symbolic stack trace.
- final dwarf = Dwarf.fromFile(scriptDwarfSnapshot);
- // We are generating unstripped snapshots, so the snapshot should include
- // the appropriate DWARF information.
+ final dwarf = Dwarf.fromFile(scriptDwarfDebugInfo);
assert(dwarf != null);
final translatedDwarfTrace1 = await Stream.fromIterable(dwarfTrace1)
.transform(DwarfStackTraceDecoder(dwarf))
diff --git a/runtime/vm/BUILD.gn b/runtime/vm/BUILD.gn
index bac066b..e3b2a7a 100644
--- a/runtime/vm/BUILD.gn
+++ b/runtime/vm/BUILD.gn
@@ -90,11 +90,23 @@
"*_test.cc",
"*_test.h",
])
- sources = vm_sources + rebase_path(compiler_sources, ".", "./compiler/") +
+ sources = vm_sources + rebase_path(compiler_api_sources, ".", "./compiler/") +
+ rebase_path(disassembler_sources, ".", "./compiler/") +
rebase_path(heap_sources, ".", "./heap/")
include_dirs = [ ".." ]
}
+library_for_all_configs_with_compiler("libdart_compiler") {
+ target_type = "source_set"
+ public_configs = [ ":libdart_vm_config" ]
+ set_sources_assignment_filter([
+ "*_test.cc",
+ "*_test.h",
+ ])
+ sources = rebase_path(compiler_sources, ".", "./compiler/")
+ include_dirs = [ ".." ]
+}
+
library_for_all_configs("libdart_lib") {
target_type = "source_set"
if (is_fuchsia) {
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index cefcf0b..a53cf86 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1519,7 +1519,7 @@
// The [HeapIterationScope] also safepoints all threads.
HeapIterationScope his(T);
- IG->class_table()->Remap(old_to_new_cid);
+ IG->shared_class_table()->Remap(old_to_new_cid);
IG->ForEachIsolate(
[&](Isolate* I) {
I->set_remapping_cids(true);
diff --git a/runtime/vm/class_table.cc b/runtime/vm/class_table.cc
index c08e1c6..338cc0a 100644
--- a/runtime/vm/class_table.cc
+++ b/runtime/vm/class_table.cc
@@ -31,7 +31,8 @@
calloc(capacity_, sizeof(RelaxedAtomic<intptr_t>))));
} else {
// Duplicate the class table from the VM isolate.
- auto vm_shared_class_table = Dart::vm_isolate()->group()->class_table();
+ auto vm_shared_class_table =
+ Dart::vm_isolate()->group()->shared_class_table();
capacity_ = vm_shared_class_table->capacity_;
// Note that [calloc] will zero-initialize the memory.
RelaxedAtomic<intptr_t>* table = reinterpret_cast<RelaxedAtomic<intptr_t>*>(
@@ -71,6 +72,13 @@
NOT_IN_PRODUCT(free(trace_allocation_table_.load()));
}
+void ClassTable::set_table(RawClass** table) {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate != nullptr);
+ table_.store(table);
+ isolate->set_cached_class_table_table(table);
+}
+
ClassTable::ClassTable(SharedClassTable* shared_class_table)
: top_(kNumPredefinedCids),
capacity_(0),
@@ -81,6 +89,9 @@
ASSERT(kInitialCapacity >= kNumPredefinedCids);
capacity_ = kInitialCapacity;
// Note that [calloc] will zero-initialize the memory.
+ // Don't use set_table because caller is supposed to set up isolates
+ // cached copy when constructing ClassTable. Isolate::Current might not
+ // be available at this point yet.
table_.store(static_cast<RawClass**>(calloc(capacity_, sizeof(RawClass*))));
} else {
// Duplicate the class table from the VM isolate.
@@ -100,6 +111,9 @@
table[kDynamicCid] = vm_class_table->At(kDynamicCid);
table[kVoidCid] = vm_class_table->At(kVoidCid);
table[kNeverCid] = vm_class_table->At(kNeverCid);
+ // Don't use set_table because caller is supposed to set up isolates
+ // cached copy when constructing ClassTable. Isolate::Current might not
+ // be available at this point yet.
table_.store(table);
}
}
@@ -226,7 +240,7 @@
new_table[i] = 0;
}
old_class_tables_->Add(old_table);
- table_.store(new_table);
+ set_table(new_table);
capacity_ = new_capacity;
}
diff --git a/runtime/vm/class_table.h b/runtime/vm/class_table.h
index a21dd51..510927a 100644
--- a/runtime/vm/class_table.h
+++ b/runtime/vm/class_table.h
@@ -355,14 +355,6 @@
void Print();
- // Used by the generated code.
- static intptr_t table_offset() { return OFFSET_OF(ClassTable, table_); }
-
- // Used by the generated code.
- static intptr_t shared_class_table_offset() {
- return OFFSET_OF(ClassTable, shared_class_table_);
- }
-
#ifndef PRODUCT
// Describes layout of heap stats for code generation. See offset_extractor.cc
struct ArrayLayout {
@@ -387,9 +379,11 @@
friend class MarkingWeakVisitor;
friend class Scavenger;
friend class ScavengerWeakVisitor;
+ friend class Dart;
friend Isolate* CreateWithinExistingIsolateGroup(IsolateGroup* group,
const char* name,
char** error);
+ friend class Isolate; // for table()
static const int kInitialCapacity = SharedClassTable::kInitialCapacity;
static const int kCapacityIncrement = SharedClassTable::kCapacityIncrement;
@@ -397,6 +391,9 @@
void Grow(intptr_t index);
+ RawClass** table() { return table_.load(); }
+ void set_table(RawClass** table);
+
intptr_t top_;
intptr_t capacity_;
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 25a37ad..b498c29 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -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.
+#include <memory>
+
#include "vm/clustered_snapshot.h"
#include "platform/assert.h"
@@ -9,10 +11,8 @@
#include "vm/bss_relocs.h"
#include "vm/class_id.h"
#include "vm/code_observers.h"
+#include "vm/compiler/api/print_filter.h"
#include "vm/compiler/assembler/disassembler.h"
-#include "vm/compiler/backend/code_statistics.h"
-#include "vm/compiler/backend/il_printer.h"
-#include "vm/compiler/relocation.h"
#include "vm/dart.h"
#include "vm/dispatch_table.h"
#include "vm/flag_list.h"
@@ -28,6 +28,12 @@
#include "vm/timeline.h"
#include "vm/version.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/backend/code_statistics.h"
+#include "vm/compiler/backend/il_printer.h"
+#include "vm/compiler/relocation.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
#if !defined(DART_PRECOMPILED_RUNTIME)
@@ -231,7 +237,8 @@
UnboxedFieldBitmap CalculateTargetUnboxedFieldsBitmap(Serializer* s,
intptr_t class_id) {
const auto unboxed_fields_bitmap_host =
- s->isolate()->group()->class_table()->GetUnboxedFieldsMapAt(class_id);
+ s->isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+ class_id);
UnboxedFieldBitmap unboxed_fields_bitmap;
if (unboxed_fields_bitmap_host.IsEmpty() ||
@@ -349,7 +356,7 @@
}
}
- auto shared_class_table = d->isolate()->group()->class_table();
+ auto shared_class_table = d->isolate()->group()->shared_class_table();
for (intptr_t id = start_index_; id < stop_index_; id++) {
RawClass* cls = reinterpret_cast<RawClass*>(d->Ref(id));
Deserializer::InitializeHeader(cls, kClassCid, Class::InstanceSize());
@@ -1201,6 +1208,10 @@
field.InitializeGuardedListLengthInObjectOffset();
}
}
+
+ Isolate* isolate = Isolate::Current();
+ isolate->set_saved_initial_field_table(
+ std::shared_ptr<FieldTable>(isolate->field_table()->Clone()));
}
};
@@ -1511,13 +1522,30 @@
s->Push(code->ptr()->pc_descriptors_);
s->Push(code->ptr()->catch_entry_);
s->Push(code->ptr()->compressed_stackmaps_);
- if (!FLAG_dwarf_stack_traces) {
+ if (!FLAG_dwarf_stack_traces_mode) {
s->Push(code->ptr()->inlined_id_to_function_);
s->Push(code->ptr()->code_source_map_);
}
if (s->kind() == Snapshot::kFullJIT) {
s->Push(code->ptr()->deopt_info_array_);
s->Push(code->ptr()->static_calls_target_table_);
+ } else if (s->kind() == Snapshot::kFullAOT) {
+#if defined(DART_PRECOMPILER)
+ auto const calls_array = code->ptr()->static_calls_target_table_;
+ if (calls_array != Array::null()) {
+ // Some Code entries in the static calls target table may only be
+ // accessible via here, so push the Code objects.
+ auto const length = Smi::Value(calls_array->ptr()->length_);
+ for (intptr_t i = 0; i < length; i++) {
+ auto const object = calls_array->ptr()->data()[i];
+ if (object->IsHeapObject() && object->IsCode()) {
+ s->Push(object);
+ }
+ }
+ }
+#else
+ UNREACHABLE();
+#endif
}
#if !defined(PRODUCT)
s->Push(code->ptr()->return_address_metadata_);
@@ -1591,7 +1619,7 @@
WriteField(code, pc_descriptors_);
WriteField(code, catch_entry_);
WriteField(code, compressed_stackmaps_);
- if (FLAG_dwarf_stack_traces) {
+ if (FLAG_dwarf_stack_traces_mode) {
WriteFieldValue(inlined_id_to_function_, Array::null());
WriteFieldValue(code_source_map_, CodeSourceMap::null());
} else {
@@ -1729,7 +1757,7 @@
if (owner.IsFunction()) {
if ((FLAG_disassemble ||
(code.is_optimized() && FLAG_disassemble_optimized)) &&
- FlowGraphPrinter::ShouldPrint(Function::Cast(owner))) {
+ compiler::PrintFilter::ShouldPrint(Function::Cast(owner))) {
Disassembler::DisassembleCode(Function::Cast(owner), code,
code.is_optimized());
}
@@ -3022,7 +3050,8 @@
const intptr_t next_field_offset = host_next_field_offset_in_words_
<< kWordSizeLog2;
const auto unboxed_fields_bitmap =
- s->isolate()->group()->class_table()->GetUnboxedFieldsMapAt(cid_);
+ s->isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+ cid_);
intptr_t offset = Instance::NextFieldOffset();
while (offset < next_field_offset) {
// Skips unboxed fields
@@ -3058,7 +3087,8 @@
<< kWordSizeLog2;
const intptr_t count = objects_.length();
const auto unboxed_fields_bitmap =
- s->isolate()->group()->class_table()->GetUnboxedFieldsMapAt(cid_);
+ s->isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+ cid_);
for (intptr_t i = 0; i < count; i++) {
RawInstance* instance = objects_[i];
AutoTraceObject(instance);
@@ -3116,7 +3146,8 @@
Object::RoundedAllocationSize(instance_size_in_words_ * kWordSize);
const auto unboxed_fields_bitmap =
- d->isolate()->group()->class_table()->GetUnboxedFieldsMapAt(cid_);
+ d->isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+ cid_);
for (intptr_t id = start_index_; id < stop_index_; id++) {
RawInstance* instance = reinterpret_cast<RawInstance*>(d->Ref(id));
bool is_canonical = d->Read<bool>();
@@ -5521,7 +5552,7 @@
#undef DECLARE_OBJECT_STORE_FIELD
};
-void Serializer::WriteIsolateSnapshot(intptr_t num_base_objects,
+void Serializer::WriteProgramSnapshot(intptr_t num_base_objects,
ObjectStore* object_store) {
NoSafepointScope no_safepoint;
@@ -5532,7 +5563,7 @@
AddBaseObject(base_objects.At(i));
}
} else {
- // Base objects carried over from WriteVMIsolateSnapshot.
+ // Base objects carried over from WriteVMSnapshot.
num_base_objects_ += num_base_objects;
next_ref_index_ += num_base_objects;
}
@@ -5813,7 +5844,7 @@
}
ASSERT(repeat_count == 0);
- I->set_dispatch_table(table);
+ I->group()->set_dispatch_table(table);
#endif
}
@@ -6196,7 +6227,7 @@
}
}
-void Deserializer::ReadIsolateSnapshot(ObjectStore* object_store) {
+void Deserializer::ReadProgramSnapshot(ObjectStore* object_store) {
Array& refs = Array::Handle();
Prepare();
@@ -6234,8 +6265,8 @@
thread()->isolate()->class_table()->CopySizesFromClassObjects();
heap_->old_space()->EvaluateAfterLoading();
-#if defined(DEBUG)
Isolate* isolate = thread()->isolate();
+#if defined(DEBUG)
isolate->ValidateClassTable();
isolate->heap()->Verify();
#endif
@@ -6243,13 +6274,12 @@
for (intptr_t i = 0; i < num_clusters_; i++) {
clusters_[i]->PostLoad(refs, kind_, zone_);
}
- object_store->PostLoad();
+ isolate->isolate_object_store()->PreallocateObjects();
// Setup native resolver for bootstrap impl.
Bootstrap::SetupNativeResolver();
}
-
#if !defined(DART_PRECOMPILED_RUNTIME)
FullSnapshotWriter::FullSnapshotWriter(Snapshot::Kind kind,
uint8_t** vm_snapshot_data_buffer,
@@ -6324,8 +6354,8 @@
return num_objects;
}
-void FullSnapshotWriter::WriteIsolateSnapshot(intptr_t num_base_objects) {
- TIMELINE_DURATION(thread(), Isolate, "WriteIsolateSnapshot");
+void FullSnapshotWriter::WriteProgramSnapshot(intptr_t num_base_objects) {
+ TIMELINE_DURATION(thread(), Isolate, "WriteProgramSnapshot");
Serializer serializer(thread(), kind_, isolate_snapshot_data_buffer_, alloc_,
kInitialSize, isolate_image_writer_, /*vm=*/false,
@@ -6344,7 +6374,7 @@
serializer.WriteVersionAndFeatures(false);
// Isolate snapshot roots are:
// - the object store
- serializer.WriteIsolateSnapshot(num_base_objects, object_store);
+ serializer.WriteProgramSnapshot(num_base_objects, object_store);
serializer.FillHeader(serializer.kind());
clustered_isolate_size_ = serializer.bytes_written();
@@ -6375,7 +6405,7 @@
}
if (isolate_snapshot_data_buffer() != NULL) {
- WriteIsolateSnapshot(num_base_objects);
+ WriteProgramSnapshot(num_base_objects);
}
if (FLAG_print_snapshot_sizes) {
@@ -6538,7 +6568,7 @@
return ApiError::null();
}
-RawApiError* FullSnapshotReader::ReadIsolateSnapshot() {
+RawApiError* FullSnapshotReader::ReadProgramSnapshot() {
SnapshotHeaderReader header_reader(kind_, buffer_, size_);
intptr_t offset = 0;
char* error =
@@ -6564,7 +6594,7 @@
}
auto object_store = thread_->isolate()->object_store();
- deserializer.ReadIsolateSnapshot(object_store);
+ deserializer.ReadProgramSnapshot(object_store);
#if defined(DART_PRECOMPILED_RUNTIME)
if (FLAG_use_bare_instructions) {
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index 791c1cf..b7da466 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -157,7 +157,7 @@
}
intptr_t WriteVMSnapshot(const Array& symbols);
- void WriteIsolateSnapshot(intptr_t num_base_objects,
+ void WriteProgramSnapshot(intptr_t num_base_objects,
ObjectStore* object_store);
void AddVMIsolateBaseObjects();
@@ -539,7 +539,7 @@
// message otherwise.
RawApiError* VerifyImageAlignment();
- void ReadIsolateSnapshot(ObjectStore* object_store);
+ void ReadProgramSnapshot(ObjectStore* object_store);
void ReadVMSnapshot();
void AddVMIsolateBaseObjects();
@@ -682,7 +682,7 @@
Isolate* isolate() const { return thread_->isolate(); }
Heap* heap() const { return isolate()->heap(); }
- // Writes a full snapshot of the Isolate.
+ // Writes a full snapshot of the program(VM isolate, regular isolate group).
void WriteFullSnapshot();
intptr_t VmIsolateSnapshotSize() const { return vm_isolate_snapshot_size_; }
@@ -692,8 +692,8 @@
// Writes a snapshot of the VM Isolate.
intptr_t WriteVMSnapshot();
- // Writes a full snapshot of a regular Dart Isolate.
- void WriteIsolateSnapshot(intptr_t num_base_objects);
+ // Writes a full snapshot of regular Dart isolate group.
+ void WriteProgramSnapshot(intptr_t num_base_objects);
Thread* thread_;
Snapshot::Kind kind_;
@@ -724,7 +724,7 @@
~FullSnapshotReader() {}
RawApiError* ReadVMSnapshot();
- RawApiError* ReadIsolateSnapshot();
+ RawApiError* ReadProgramSnapshot();
private:
RawApiError* ConvertToApiError(char* message);
diff --git a/runtime/vm/code_comments.cc b/runtime/vm/code_comments.cc
index 74f8458..d1344d3 100644
--- a/runtime/vm/code_comments.cc
+++ b/runtime/vm/code_comments.cc
@@ -1,14 +1,13 @@
// Copyright (c) 2019, 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.
+#if !defined(DART_PRECOMPILED_RUNTIME) && \
+ (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
#include "vm/code_comments.h"
namespace dart {
-#if !defined(DART_PRECOMPILED_RUNTIME) && \
- (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
-
const Code::Comments& CreateCommentsFrom(compiler::Assembler* assembler) {
const auto& comments = assembler->comments();
Code::Comments& result = Code::Comments::New(comments.length());
@@ -21,7 +20,6 @@
return result;
}
+} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME) && \
// (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
-
-} // namespace dart
diff --git a/runtime/vm/code_comments.h b/runtime/vm/code_comments.h
index 9c86524..18f0e75 100644
--- a/runtime/vm/code_comments.h
+++ b/runtime/vm/code_comments.h
@@ -5,15 +5,15 @@
#ifndef RUNTIME_VM_CODE_COMMENTS_H_
#define RUNTIME_VM_CODE_COMMENTS_H_
+#if !defined(DART_PRECOMPILED_RUNTIME) && \
+ (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
+
#include "vm/code_observers.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/object.h"
namespace dart {
-#if !defined(DART_PRECOMPILED_RUNTIME) && \
- (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
-
class CodeCommentsWrapper final : public CodeComments {
public:
explicit CodeCommentsWrapper(const Code::Comments& comments)
@@ -37,9 +37,9 @@
const Code::Comments& CreateCommentsFrom(compiler::Assembler* assembler);
-#endif // !defined(DART_PRECOMPILED_RUNTIME) && \
- // (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
} // namespace dart
+#endif // !defined(DART_PRECOMPILED_RUNTIME) && \
+ // (!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
#endif // RUNTIME_VM_CODE_COMMENTS_H_
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index 5136d03..d773325 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -4,7 +4,7 @@
#include "vm/code_descriptors.h"
-#include "vm/compiler/compiler_state.h"
+#include "vm/compiler/api/deopt_id.h"
#include "vm/log.h"
#include "vm/object_store.h"
#include "vm/zone_text_buffer.h"
diff --git a/runtime/vm/code_patcher.h b/runtime/vm/code_patcher.h
index fa5c521..b9cc1c9 100644
--- a/runtime/vm/code_patcher.h
+++ b/runtime/vm/code_patcher.h
@@ -61,6 +61,11 @@
const Code& caller_code,
const Object& data,
const Code& target);
+ static void PatchInstanceCallAtWithMutatorsStopped(Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target);
// Return target of an unoptimized static call and its ICData object
// (calls target via a stub).
@@ -78,6 +83,11 @@
const Code& caller_code,
const Object& data,
const Code& target);
+ static void PatchSwitchableCallAtWithMutatorsStopped(Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target);
static RawObject* GetSwitchableCallDataAt(uword return_address,
const Code& caller_code);
static RawCode* GetSwitchableCallTargetAt(uword return_address,
diff --git a/runtime/vm/code_patcher_arm.cc b/runtime/vm/code_patcher_arm.cc
index 024a6ab..52bdb2c 100644
--- a/runtime/vm/code_patcher_arm.cc
+++ b/runtime/vm/code_patcher_arm.cc
@@ -7,7 +7,6 @@
#include "vm/code_patcher.h"
-#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/instructions.h"
#include "vm/object.h"
@@ -47,6 +46,19 @@
const Code& caller_code,
const Object& data,
const Code& target) {
+ auto thread = Thread::Current();
+ thread->isolate_group()->RunWithStoppedMutators([&]() {
+ PatchInstanceCallAtWithMutatorsStopped(thread, return_address, caller_code,
+ data, target);
+ });
+}
+
+void CodePatcher::PatchInstanceCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
ASSERT(caller_code.ContainsInstructionAt(return_address));
ICCallPattern call(return_address, caller_code);
call.SetData(data);
@@ -70,6 +82,20 @@
const Code& caller_code,
const Object& data,
const Code& target) {
+ auto thread = Thread::Current();
+ // Ensure all threads are suspended as we update data and target pair.
+ thread->isolate_group()->RunWithStoppedMutators([&]() {
+ PatchSwitchableCallAtWithMutatorsStopped(thread, return_address,
+ caller_code, data, target);
+ });
+}
+
+void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
ASSERT(caller_code.ContainsInstructionAt(return_address));
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
BareSwitchableCallPattern call(return_address, caller_code);
@@ -110,10 +136,12 @@
const Code& code,
NativeFunction target,
const Code& trampoline) {
- ASSERT(code.ContainsInstructionAt(return_address));
- NativeCallPattern call(return_address, code);
- call.set_target(trampoline);
- call.set_native_function(target);
+ Thread::Current()->isolate_group()->RunWithStoppedMutators([&]() {
+ ASSERT(code.ContainsInstructionAt(return_address));
+ NativeCallPattern call(return_address, code);
+ call.set_target(trampoline);
+ call.set_native_function(target);
+ });
}
RawCode* CodePatcher::GetNativeCallAt(uword return_address,
diff --git a/runtime/vm/code_patcher_arm64.cc b/runtime/vm/code_patcher_arm64.cc
index ce3c4dc..abffa1b 100644
--- a/runtime/vm/code_patcher_arm64.cc
+++ b/runtime/vm/code_patcher_arm64.cc
@@ -82,6 +82,19 @@
const Code& caller_code,
const Object& data,
const Code& target) {
+ auto thread = Thread::Current();
+ thread->isolate_group()->RunWithStoppedMutators([&]() {
+ PatchInstanceCallAtWithMutatorsStopped(thread, return_address, caller_code,
+ data, target);
+ });
+}
+
+void CodePatcher::PatchInstanceCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
ASSERT(caller_code.ContainsInstructionAt(return_address));
ICCallPattern call(return_address, caller_code);
call.SetData(data);
@@ -105,6 +118,20 @@
const Code& caller_code,
const Object& data,
const Code& target) {
+ auto thread = Thread::Current();
+ // Ensure all threads are suspended as we update data and target pair.
+ thread->isolate_group()->RunWithStoppedMutators([&]() {
+ PatchSwitchableCallAtWithMutatorsStopped(thread, return_address,
+ caller_code, data, target);
+ });
+}
+
+void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
ASSERT(caller_code.ContainsInstructionAt(return_address));
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
BareSwitchableCallPattern call(return_address, caller_code);
@@ -145,10 +172,12 @@
const Code& caller_code,
NativeFunction target,
const Code& trampoline) {
- ASSERT(caller_code.ContainsInstructionAt(return_address));
- NativeCallPattern call(return_address, caller_code);
- call.set_target(trampoline);
- call.set_native_function(target);
+ Thread::Current()->isolate_group()->RunWithStoppedMutators([&]() {
+ ASSERT(caller_code.ContainsInstructionAt(return_address));
+ NativeCallPattern call(return_address, caller_code);
+ call.set_target(trampoline);
+ call.set_native_function(target);
+ });
}
RawCode* CodePatcher::GetNativeCallAt(uword return_address,
diff --git a/runtime/vm/code_patcher_ia32.cc b/runtime/vm/code_patcher_ia32.cc
index 7e97f67..a82d378 100644
--- a/runtime/vm/code_patcher_ia32.cc
+++ b/runtime/vm/code_patcher_ia32.cc
@@ -6,8 +6,6 @@
#if defined(TARGET_ARCH_IA32)
#include "vm/code_patcher.h"
-#include "vm/compiler/assembler/assembler.h"
-#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/cpu.h"
#include "vm/dart_entry.h"
#include "vm/instructions.h"
@@ -212,16 +210,26 @@
const Object& data,
const Code& target) {
auto thread = Thread::Current();
+ thread->isolate_group()->RunWithStoppedMutators([&]() {
+ PatchInstanceCallAtWithMutatorsStopped(thread, return_address, caller_code,
+ data, target);
+ });
+}
+
+void CodePatcher::PatchInstanceCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
auto zone = thread->zone();
ASSERT(caller_code.ContainsInstructionAt(return_address));
const Instructions& instrs =
Instructions::Handle(zone, caller_code.instructions());
- thread->isolate_group()->RunWithStoppedMutators([&]() {
- WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
- InstanceCall call(return_address);
- call.set_data(data);
- call.set_target(target);
- });
+ WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
+ InstanceCall call(return_address);
+ call.set_data(data);
+ call.set_target(target);
}
RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,
@@ -245,6 +253,16 @@
UNREACHABLE();
}
+void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
+ // Switchable instance calls only generated for precompilation.
+ UNREACHABLE();
+}
+
RawCode* CodePatcher::GetSwitchableCallTargetAt(uword return_address,
const Code& caller_code) {
// Switchable instance calls only generated for precompilation.
diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc
index e67d187..b4c241c 100644
--- a/runtime/vm/code_patcher_x64.cc
+++ b/runtime/vm/code_patcher_x64.cc
@@ -6,8 +6,6 @@
#if defined(TARGET_ARCH_X64)
#include "vm/code_patcher.h"
-#include "vm/compiler/assembler/assembler.h"
-#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/cpu.h"
#include "vm/dart_entry.h"
#include "vm/instructions.h"
@@ -447,6 +445,19 @@
const Code& caller_code,
const Object& data,
const Code& target) {
+ auto thread = Thread::Current();
+ thread->isolate_group()->RunWithStoppedMutators([&]() {
+ PatchInstanceCallAtWithMutatorsStopped(thread, return_address, caller_code,
+ data, target);
+ });
+}
+
+void CodePatcher::PatchInstanceCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
ASSERT(caller_code.ContainsInstructionAt(return_address));
InstanceCall call(return_address, caller_code);
call.set_data(data);
@@ -474,6 +485,20 @@
const Code& caller_code,
const Object& data,
const Code& target) {
+ auto thread = Thread::Current();
+ // Ensure all threads are suspended as we update data and target pair.
+ thread->isolate_group()->RunWithStoppedMutators([&]() {
+ PatchSwitchableCallAtWithMutatorsStopped(thread, return_address,
+ caller_code, data, target);
+ });
+}
+
+void CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ Thread* thread,
+ uword return_address,
+ const Code& caller_code,
+ const Object& data,
+ const Code& target) {
ASSERT(caller_code.ContainsInstructionAt(return_address));
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
BareSwitchableCall call(return_address, caller_code);
@@ -514,10 +539,12 @@
const Code& caller_code,
NativeFunction target,
const Code& trampoline) {
- ASSERT(caller_code.ContainsInstructionAt(return_address));
- NativeCall call(return_address, caller_code);
- call.set_target(trampoline);
- call.set_native_function(target);
+ Thread::Current()->isolate_group()->RunWithStoppedMutators([&]() {
+ ASSERT(caller_code.ContainsInstructionAt(return_address));
+ NativeCall call(return_address, caller_code);
+ call.set_target(trampoline);
+ call.set_native_function(target);
+ });
}
RawCode* CodePatcher::GetNativeCallAt(uword return_address,
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index ec30d5d..0854325 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -353,42 +353,38 @@
// We don't want the Array backing for any mappings in the snapshot,
// only the pools themselves.
I->object_store()->set_llvm_constant_hash_table(Array::null_array());
+
+ // Keep any functions, classes, etc. referenced from the LLVM pools,
+ // even if they could have been dropped due to not being otherwise
+ // needed at runtime.
+ const auto& constant_pool = GrowableObjectArray::Handle(
+ Z, I->object_store()->llvm_constant_pool());
+ auto& object = Object::Handle(Z);
+ for (intptr_t i = 0; i < constant_pool.Length(); i++) {
+ object = constant_pool.At(i);
+ if (object.IsNull()) continue;
+ if (object.IsInstance()) {
+ AddConstObject(Instance::Cast(object));
+ } else if (object.IsField()) {
+ AddField(Field::Cast(object));
+ } else if (object.IsFunction()) {
+ AddFunction(Function::Cast(object));
+ }
+ }
+
+ const auto& function_pool = GrowableObjectArray::Handle(
+ Z, I->object_store()->llvm_function_pool());
+ auto& function = Function::Handle(Z);
+ for (intptr_t i = 0; i < function_pool.Length(); i++) {
+ function ^= function_pool.At(i);
+ AddFunction(function);
+ }
}
}
TraceForRetainedFunctions();
-
- if (FLAG_use_bare_instructions && FLAG_use_table_dispatch) {
- // Build the entries used to serialize the dispatch table before
- // dropping functions, as we may clear references to Code objects.
- const auto& entries =
- Array::Handle(Z, dispatch_table_generator_->BuildCodeArray());
- I->object_store()->set_dispatch_table_code_entries(entries);
- // Delete the dispatch table generator to ensure there's no attempt
- // to add new entries after this point.
- delete dispatch_table_generator_;
- dispatch_table_generator_ = nullptr;
- if (!FLAG_retain_dispatched_functions && FLAG_trace_precompiler) {
- FunctionSet printed(
- HashTables::New<FunctionSet>(/*initial_capacity=*/1024));
- auto& code = Code::Handle(Z);
- auto& function = Function::Handle(Z);
- for (intptr_t i = 0; i < entries.Length(); i++) {
- code = Code::RawCast(entries.At(i));
- if (code.IsNull()) continue;
- if (!code.IsFunctionCode()) continue;
- function = code.function();
- ASSERT(!function.IsNull());
- if (printed.ContainsKey(function)) continue;
- if (functions_to_retain_.ContainsKey(function)) continue;
- THR_Print(
- "Dispatch table references code for function to drop: %s\n",
- function.ToLibNamePrefixedQualifiedCString());
- printed.Insert(function);
- }
- printed.Release();
- }
- }
+ FinalizeDispatchTable();
+ ReplaceFunctionPCRelativeCallEntries();
DropFunctions();
DropFields();
@@ -682,7 +678,9 @@
for (auto& view : static_calls) {
entry = view.Get<Code::kSCallTableFunctionTarget>();
if (entry.IsFunction()) {
- AddFunction(Function::Cast(entry));
+ AddFunction(Function::Cast(entry), FLAG_retain_function_objects);
+ ASSERT(view.Get<Code::kSCallTableCodeTarget>() == Code::null());
+ continue;
}
entry = view.Get<Code::kSCallTableCodeTarget>();
if (entry.IsCode() && Code::Cast(entry).IsAllocationStubCode()) {
@@ -1304,7 +1302,7 @@
AddFunction(function);
}
if (IsHitByTableSelector(function)) {
- AddFunction(function, FLAG_retain_dispatched_functions);
+ AddFunction(function, FLAG_retain_function_objects);
}
bool found_metadata = false;
@@ -1528,6 +1526,85 @@
}
}
+void Precompiler::FinalizeDispatchTable() {
+ if (!FLAG_use_bare_instructions || !FLAG_use_table_dispatch) return;
+ // Build the entries used to serialize the dispatch table before
+ // dropping functions, as we may clear references to Code objects.
+ const auto& entries =
+ Array::Handle(Z, dispatch_table_generator_->BuildCodeArray());
+ I->object_store()->set_dispatch_table_code_entries(entries);
+ // Delete the dispatch table generator to ensure there's no attempt
+ // to add new entries after this point.
+ delete dispatch_table_generator_;
+ dispatch_table_generator_ = nullptr;
+
+ if (FLAG_retain_function_objects || !FLAG_trace_precompiler) return;
+
+ FunctionSet printed(HashTables::New<FunctionSet>(/*initial_capacity=*/1024));
+ auto& code = Code::Handle(Z);
+ auto& function = Function::Handle(Z);
+ for (intptr_t i = 0; i < entries.Length(); i++) {
+ code = Code::RawCast(entries.At(i));
+ if (code.IsNull()) continue;
+ if (!code.IsFunctionCode()) continue;
+ function = code.function();
+ ASSERT(!function.IsNull());
+ if (printed.ContainsKey(function)) continue;
+ if (functions_to_retain_.ContainsKey(function)) continue;
+ THR_Print("Dispatch table references code for function to drop: %s\n",
+ function.ToLibNamePrefixedQualifiedCString());
+ printed.Insert(function);
+ }
+ printed.Release();
+}
+
+void Precompiler::ReplaceFunctionPCRelativeCallEntries() {
+ class StaticCallTableEntryFixer : public CodeVisitor {
+ public:
+ explicit StaticCallTableEntryFixer(Zone* zone)
+ : table_(Array::Handle(zone)),
+ kind_and_offset_(Smi::Handle(zone)),
+ target_function_(Function::Handle(zone)),
+ target_code_(Code::Handle(zone)) {}
+
+ void VisitCode(const Code& code) {
+ if (!code.IsFunctionCode()) return;
+ table_ = code.static_calls_target_table();
+ StaticCallsTable static_calls(table_);
+ for (auto& view : static_calls) {
+ kind_and_offset_ = view.Get<Code::kSCallTableKindAndOffset>();
+ auto const kind = Code::KindField::decode(kind_and_offset_.Value());
+ if (kind != Code::kPcRelativeCall) continue;
+
+ target_function_ = view.Get<Code::kSCallTableFunctionTarget>();
+ if (target_function_.IsNull()) continue;
+
+ ASSERT(view.Get<Code::kSCallTableCodeTarget>() == Code::null());
+ ASSERT(target_function_.HasCode());
+ target_code_ = target_function_.CurrentCode();
+ ASSERT(!target_code_.IsStubCode());
+ view.Set<Code::kSCallTableCodeTarget>(target_code_);
+ view.Set<Code::kSCallTableFunctionTarget>(Object::null_function());
+ if (FLAG_trace_precompiler) {
+ THR_Print("Updated static call entry to %s in \"%s\"\n",
+ target_function_.ToFullyQualifiedCString(),
+ code.ToCString());
+ }
+ }
+ }
+
+ private:
+ Array& table_;
+ Smi& kind_and_offset_;
+ Function& target_function_;
+ Code& target_code_;
+ };
+
+ HANDLESCOPE(T);
+ StaticCallTableEntryFixer visitor(Z);
+ ProgramVisitor::WalkProgram(Z, I, &visitor);
+}
+
void Precompiler::DropFunctions() {
Library& lib = Library::Handle(Z);
Class& cls = Class::Handle(Z);
@@ -2006,7 +2083,6 @@
void Precompiler::DropClasses() {
Class& cls = Class::Handle(Z);
Array& constants = Array::Handle(Z);
- const Script& null_script = Script::Handle(Z);
// We are about to remove classes from the class table. For this to be safe,
// there must be no instances of these classes on the heap, not even
@@ -2049,7 +2125,6 @@
class_table->Unregister(cid);
cls.set_id(kIllegalCid); // We check this when serializing.
- cls.set_script(null_script);
}
}
@@ -2060,7 +2135,6 @@
Library::Handle(Z, I->object_store()->root_library());
Library& lib = Library::Handle(Z);
Class& toplevel_class = Class::Handle(Z);
- const Script& null_script = Script::Handle(Z);
for (intptr_t i = 0; i < libraries_.Length(); i++) {
lib ^= libraries_.At(i);
@@ -2098,7 +2172,6 @@
I->class_table()->Unregister(toplevel_class.id());
toplevel_class.set_id(kIllegalCid); // We check this when serializing.
- toplevel_class.set_script(null_script);
dropped_library_count_++;
lib.set_index(-1);
diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h
index 36a600d..8de467a 100644
--- a/runtime/vm/compiler/aot/precompiler.h
+++ b/runtime/vm/compiler/aot/precompiler.h
@@ -257,6 +257,8 @@
void AttachOptimizedTypeTestingStub();
void TraceForRetainedFunctions();
+ void FinalizeDispatchTable();
+ void ReplaceFunctionPCRelativeCallEntries();
void DropFunctions();
void DropFields();
void TraceTypesFromRetainedClasses();
diff --git a/runtime/vm/compiler/api/deopt_id.h b/runtime/vm/compiler/api/deopt_id.h
new file mode 100644
index 0000000..c203e45
--- /dev/null
+++ b/runtime/vm/compiler/api/deopt_id.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef RUNTIME_VM_COMPILER_API_DEOPT_ID_H_
+#define RUNTIME_VM_COMPILER_API_DEOPT_ID_H_
+
+#include "platform/allocation.h"
+
+namespace dart {
+
+// Deoptimization Id logic.
+//
+// Deoptimization ids are used to refer to deoptimization points, at which
+// control can enter unoptimized code from the optimized version of the code.
+//
+// Note: any instruction that does a call has two deoptimization points,
+// one before the call and one after the call - so that we could deoptimize
+// to either before or after the call depending on whether the same call
+// already occured in the optimized code (and potentially produced
+// observable side-effects) or not.
+//
+// To simplify implementation we always allocate two deopt ids (one for before
+// point and one for the after point).
+class DeoptId : public AllStatic {
+ public:
+ static constexpr intptr_t kNone = -1;
+
+ static inline intptr_t Next(intptr_t deopt_id) { return deopt_id + kStep; }
+
+ static inline intptr_t ToDeoptAfter(intptr_t deopt_id) {
+ ASSERT(IsDeoptBefore(deopt_id));
+ return deopt_id + kAfterOffset;
+ }
+
+ static inline bool IsDeoptBefore(intptr_t deopt_id) {
+ return (deopt_id % kStep) == kBeforeOffset;
+ }
+
+ static inline bool IsDeoptAfter(intptr_t deopt_id) {
+ return (deopt_id % kStep) == kAfterOffset;
+ }
+
+ private:
+ static constexpr intptr_t kStep = 2;
+ static constexpr intptr_t kBeforeOffset = 0;
+ static constexpr intptr_t kAfterOffset = 1;
+};
+
+} // namespace dart
+
+#endif // RUNTIME_VM_COMPILER_API_DEOPT_ID_H_
diff --git a/runtime/vm/compiler/api/print_filter.cc b/runtime/vm/compiler/api/print_filter.cc
new file mode 100644
index 0000000..ee2a06f
--- /dev/null
+++ b/runtime/vm/compiler/api/print_filter.cc
@@ -0,0 +1,70 @@
+// 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.
+#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
+
+#include "vm/compiler/api/print_filter.h"
+
+#include "vm/flags.h"
+#include "vm/object.h"
+
+namespace dart {
+
+DEFINE_FLAG(charp,
+ print_flow_graph_filter,
+ NULL,
+ "Print only IR of functions with matching names");
+
+namespace compiler {
+
+// Checks whether function's name matches the given filter, which is
+// a comma-separated list of strings.
+static bool PassesFilter(const char* filter, const Function& function) {
+ if (filter == NULL) {
+ return true;
+ }
+
+ char* save_ptr; // Needed for strtok_r.
+ const char* scrubbed_name =
+ String::Handle(function.QualifiedScrubbedName()).ToCString();
+ const char* function_name = function.ToFullyQualifiedCString();
+ intptr_t function_name_len = strlen(function_name);
+
+ intptr_t len = strlen(filter) + 1; // Length with \0.
+ char* filter_buffer = new char[len];
+ strncpy(filter_buffer, filter, len); // strtok modifies arg 1.
+ char* token = strtok_r(filter_buffer, ",", &save_ptr);
+ bool found = false;
+ while (token != NULL) {
+ if ((strstr(function_name, token) != NULL) ||
+ (strstr(scrubbed_name, token) != NULL)) {
+ found = true;
+ break;
+ }
+ const intptr_t token_len = strlen(token);
+ if (token[token_len - 1] == '%') {
+ if (function_name_len > token_len) {
+ const char* suffix =
+ function_name + (function_name_len - token_len + 1);
+ if (strncmp(suffix, token, token_len - 1) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+ token = strtok_r(NULL, ",", &save_ptr);
+ }
+ delete[] filter_buffer;
+
+ return found;
+}
+
+bool PrintFilter::ShouldPrint(const Function& function) {
+ return PassesFilter(FLAG_print_flow_graph_filter, function);
+}
+
+} // namespace compiler
+
+} // namespace dart
+
+#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
diff --git a/runtime/vm/compiler/api/print_filter.h b/runtime/vm/compiler/api/print_filter.h
new file mode 100644
index 0000000..1eea14a
--- /dev/null
+++ b/runtime/vm/compiler/api/print_filter.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef RUNTIME_VM_COMPILER_API_PRINT_FILTER_H_
+#define RUNTIME_VM_COMPILER_API_PRINT_FILTER_H_
+#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
+
+#include "platform/allocation.h"
+
+namespace dart {
+
+class Function;
+
+namespace compiler {
+
+class PrintFilter : public AllStatic {
+ public:
+ static bool ShouldPrint(const Function& function);
+};
+
+} // namespace compiler
+
+} // namespace dart
+
+#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
+#endif // RUNTIME_VM_COMPILER_API_PRINT_FILTER_H_
diff --git a/runtime/vm/compiler/api/type_check_mode.h b/runtime/vm/compiler/api/type_check_mode.h
new file mode 100644
index 0000000..7710607
--- /dev/null
+++ b/runtime/vm/compiler/api/type_check_mode.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef RUNTIME_VM_COMPILER_API_TYPE_CHECK_MODE_H_
+#define RUNTIME_VM_COMPILER_API_TYPE_CHECK_MODE_H_
+
+namespace dart {
+
+// Invocation mode for TypeCheck runtime entry that describes
+// where we are calling it from.
+enum TypeCheckMode {
+ // TypeCheck is invoked from LazySpecializeTypeTest stub.
+ // It should replace stub on the type with a specialized version.
+ kTypeCheckFromLazySpecializeStub,
+
+ // TypeCheck is invoked from the SlowTypeTest stub.
+ // This means that cache can be lazily created (if needed)
+ // and dst_name can be fetched from the pool.
+ kTypeCheckFromSlowStub,
+
+ // TypeCheck is invoked from normal inline AssertAssignable.
+ // Both cache and dst_name must be already populated.
+ kTypeCheckFromInline
+};
+
+} // namespace dart
+
+#endif // RUNTIME_VM_COMPILER_API_TYPE_CHECK_MODE_H_
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc
index 6fe378e..86679cb 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc
@@ -1583,21 +1583,24 @@
__ b(¬_double, NE);
__ LoadIsolate(R0);
- __ LoadFromOffset(kWord, R0, R0, target::Isolate::object_store_offset());
+ __ LoadFromOffset(kWord, R0, R0,
+ target::Isolate::cached_object_store_offset());
__ LoadFromOffset(kWord, R0, R0, target::ObjectStore::double_type_offset());
__ Ret();
__ Bind(¬_double);
JumpIfNotInteger(assembler, R1, R0, ¬_integer);
__ LoadIsolate(R0);
- __ LoadFromOffset(kWord, R0, R0, target::Isolate::object_store_offset());
+ __ LoadFromOffset(kWord, R0, R0,
+ target::Isolate::cached_object_store_offset());
__ LoadFromOffset(kWord, R0, R0, target::ObjectStore::int_type_offset());
__ Ret();
__ Bind(¬_integer);
JumpIfNotString(assembler, R1, R0, &use_declaration_type);
__ LoadIsolate(R0);
- __ LoadFromOffset(kWord, R0, R0, target::Isolate::object_store_offset());
+ __ LoadFromOffset(kWord, R0, R0,
+ target::Isolate::cached_object_store_offset());
__ LoadFromOffset(kWord, R0, R0, target::ObjectStore::string_type_offset());
__ Ret();
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index db838ee..aaf35b7 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -1648,21 +1648,21 @@
__ b(¬_double, NE);
__ LoadIsolate(R0);
- __ LoadFromOffset(R0, R0, target::Isolate::object_store_offset());
+ __ LoadFromOffset(R0, R0, target::Isolate::cached_object_store_offset());
__ LoadFromOffset(R0, R0, target::ObjectStore::double_type_offset());
__ ret();
__ Bind(¬_double);
JumpIfNotInteger(assembler, R1, R0, ¬_integer);
__ LoadIsolate(R0);
- __ LoadFromOffset(R0, R0, target::Isolate::object_store_offset());
+ __ LoadFromOffset(R0, R0, target::Isolate::cached_object_store_offset());
__ LoadFromOffset(R0, R0, target::ObjectStore::int_type_offset());
__ ret();
__ Bind(¬_integer);
JumpIfNotString(assembler, R1, R0, &use_declaration_type);
__ LoadIsolate(R0);
- __ LoadFromOffset(R0, R0, target::Isolate::object_store_offset());
+ __ LoadFromOffset(R0, R0, target::Isolate::cached_object_store_offset());
__ LoadFromOffset(R0, R0, target::ObjectStore::string_type_offset());
__ ret();
diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
index c4fb55b..edcc866 100644
--- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
@@ -1682,7 +1682,7 @@
__ j(NOT_EQUAL, ¬_double);
__ LoadIsolate(EAX);
- __ movl(EAX, Address(EAX, target::Isolate::object_store_offset()));
+ __ movl(EAX, Address(EAX, target::Isolate::cached_object_store_offset()));
__ movl(EAX, Address(EAX, target::ObjectStore::double_type_offset()));
__ ret();
@@ -1692,7 +1692,7 @@
JumpIfNotInteger(assembler, EAX, ¬_integer);
__ LoadIsolate(EAX);
- __ movl(EAX, Address(EAX, target::Isolate::object_store_offset()));
+ __ movl(EAX, Address(EAX, target::Isolate::cached_object_store_offset()));
__ movl(EAX, Address(EAX, target::ObjectStore::int_type_offset()));
__ ret();
@@ -1703,7 +1703,7 @@
JumpIfNotString(assembler, EAX, &use_declaration_type);
__ LoadIsolate(EAX);
- __ movl(EAX, Address(EAX, target::Isolate::object_store_offset()));
+ __ movl(EAX, Address(EAX, target::Isolate::cached_object_store_offset()));
__ movl(EAX, Address(EAX, target::ObjectStore::string_type_offset()));
__ ret();
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index 09b3e10..c4dc1b6 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -1594,7 +1594,7 @@
__ j(NOT_EQUAL, ¬_double);
__ LoadIsolate(RAX);
- __ movq(RAX, Address(RAX, target::Isolate::object_store_offset()));
+ __ movq(RAX, Address(RAX, target::Isolate::cached_object_store_offset()));
__ movq(RAX, Address(RAX, target::ObjectStore::double_type_offset()));
__ ret();
@@ -1604,7 +1604,7 @@
JumpIfNotInteger(assembler, RAX, ¬_integer);
__ LoadIsolate(RAX);
- __ movq(RAX, Address(RAX, target::Isolate::object_store_offset()));
+ __ movq(RAX, Address(RAX, target::Isolate::cached_object_store_offset()));
__ movq(RAX, Address(RAX, target::ObjectStore::int_type_offset()));
__ ret();
@@ -1615,7 +1615,7 @@
JumpIfNotString(assembler, RAX, &use_declaration_type);
__ LoadIsolate(RAX);
- __ movq(RAX, Address(RAX, target::Isolate::object_store_offset()));
+ __ movq(RAX, Address(RAX, target::Isolate::cached_object_store_offset()));
__ movq(RAX, Address(RAX, target::ObjectStore::string_type_offset()));
__ ret();
diff --git a/runtime/vm/compiler/assembler/assembler_arm.cc b/runtime/vm/compiler/assembler/assembler_arm.cc
index 9c0caf9..35f8ced 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm.cc
@@ -2004,8 +2004,8 @@
void Assembler::LoadClassById(Register result, Register class_id) {
ASSERT(result != class_id);
- const intptr_t table_offset = target::Isolate::class_table_offset() +
- target::ClassTable::table_offset();
+ const intptr_t table_offset =
+ target::Isolate::cached_class_table_table_offset();
LoadIsolate(result);
LoadFromOffset(kWord, result, result, table_offset);
@@ -3529,8 +3529,7 @@
ASSERT(cid > 0);
const intptr_t shared_table_offset =
- target::Isolate::class_table_offset() +
- target::ClassTable::shared_class_table_offset();
+ target::Isolate::shared_class_table_offset();
const intptr_t table_offset =
target::SharedClassTable::class_heap_stats_table_offset();
const intptr_t class_offset = target::ClassTable::ClassOffsetFor(cid);
diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h
index bd79c03..81ed9f6 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.h
+++ b/runtime/vm/compiler/assembler/assembler_arm.h
@@ -589,8 +589,6 @@
B4 | (imm16 & 0xf);
}
- static uword GetBreakInstructionFiller() { return BkptEncoding(0); }
-
// Floating point instructions (VFPv3-D16 and VFPv3-D32 profiles).
void vmovsr(SRegister sn, Register rt, Condition cond = AL);
void vmovrs(Register rt, SRegister sn, Condition cond = AL);
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index 422ba5a..a22a4fa 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -1155,8 +1155,8 @@
void Assembler::LoadClassById(Register result, Register class_id) {
ASSERT(result != class_id);
- const intptr_t table_offset = target::Isolate::class_table_offset() +
- target::ClassTable::table_offset();
+ const intptr_t table_offset =
+ target::Isolate::cached_class_table_table_offset();
LoadIsolate(result);
LoadFromOffset(result, result, table_offset);
@@ -1631,8 +1631,7 @@
ASSERT(cid > 0);
const intptr_t shared_table_offset =
- target::Isolate::class_table_offset() +
- target::ClassTable::shared_class_table_offset();
+ target::Isolate::shared_class_table_offset();
const intptr_t table_offset =
target::SharedClassTable::class_heap_stats_table_offset();
const intptr_t class_offset = target::ClassTable::ClassOffsetFor(cid);
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 26ab378..071bb36 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1034,11 +1034,6 @@
// Breakpoint.
void brk(uint16_t imm) { EmitExceptionGenOp(BRK, imm); }
- static uword GetBreakInstructionFiller() {
- const intptr_t encoding = ExceptionGenOpEncoding(BRK, 0);
- return encoding << 32 | encoding;
- }
-
// Double floating point.
bool fmovdi(VRegister vd, double immd) {
int64_t imm64 = bit_cast<int64_t, double>(immd);
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index 74d3316..1ef9cda 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -2409,8 +2409,7 @@
Address state_address(kNoRegister, 0);
const intptr_t shared_table_offset =
- target::Isolate::class_table_offset() +
- target::ClassTable::shared_class_table_offset();
+ target::Isolate::shared_class_table_offset();
const intptr_t table_offset =
target::SharedClassTable::class_heap_stats_table_offset();
const intptr_t class_offset = target::ClassTable::ClassOffsetFor(cid);
@@ -2638,8 +2637,8 @@
void Assembler::LoadClassById(Register result, Register class_id) {
ASSERT(result != class_id);
- const intptr_t table_offset = target::Isolate::class_table_offset() +
- target::ClassTable::table_offset();
+ const intptr_t table_offset =
+ target::Isolate::cached_class_table_table_offset();
LoadIsolate(result);
movl(result, Address(result, table_offset));
movl(result, Address(result, class_id, TIMES_4, 0));
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.h b/runtime/vm/compiler/assembler/assembler_ia32.h
index c60968e..286b6f9 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.h
+++ b/runtime/vm/compiler/assembler/assembler_ia32.h
@@ -549,8 +549,6 @@
void int3();
void hlt();
- static uword GetBreakInstructionFiller() { return 0xCCCCCCCC; }
-
void j(Condition condition, Label* label, bool near = kFarJump);
void j(Condition condition, const ExternalLabel* label);
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index 60802f9..a7b9cb5 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -1864,8 +1864,7 @@
bool near_jump) {
ASSERT(cid > 0);
const intptr_t shared_table_offset =
- target::Isolate::class_table_offset() +
- target::ClassTable::shared_class_table_offset();
+ target::Isolate::shared_class_table_offset();
const intptr_t table_offset =
target::SharedClassTable::class_heap_stats_table_offset();
const intptr_t class_offset = target::ClassTable::ClassOffsetFor(cid);
@@ -2139,8 +2138,8 @@
void Assembler::LoadClassById(Register result, Register class_id) {
ASSERT(result != class_id);
- const intptr_t table_offset = target::Isolate::class_table_offset() +
- target::ClassTable::table_offset();
+ const intptr_t table_offset =
+ target::Isolate::cached_class_table_table_offset();
LoadIsolate(result);
movq(result, Address(result, table_offset));
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index 0c281ca..42a17d8 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -642,8 +642,6 @@
// 'size' indicates size in bytes and must be in the range 1..8.
void nop(int size = 1);
- static uword GetBreakInstructionFiller() { return 0xCCCCCCCCCCCCCCCC; }
-
void j(Condition condition, Label* label, bool near = kFarJump);
void jmp(Register reg) { EmitUnaryL(reg, 0xFF, 4); }
void jmp(const Address& address) { EmitUnaryL(address, 0xFF, 4); }
diff --git a/runtime/vm/compiler/assembler/disassembler.cc b/runtime/vm/compiler/assembler/disassembler.cc
index 37c3743..89e9204 100644
--- a/runtime/vm/compiler/assembler/disassembler.cc
+++ b/runtime/vm/compiler/assembler/disassembler.cc
@@ -5,8 +5,6 @@
#include "vm/compiler/assembler/disassembler.h"
#include "vm/code_patcher.h"
-#include "vm/compiler/assembler/assembler.h"
-#include "vm/compiler/backend/il_printer.h"
#include "vm/deopt_instructions.h"
#include "vm/globals.h"
#include "vm/instructions.h"
@@ -18,7 +16,10 @@
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
+#if !defined(DART_PRECOMPILED_RUNTIME)
DECLARE_FLAG(bool, trace_inlining_intervals);
+#endif
+
DEFINE_FLAG(bool, trace_source_positions, false, "Source position diagnostics");
void DisassembleToStdout::ConsumeInstruction(char* hex_buffer,
@@ -416,9 +417,12 @@
}
#endif // defined(DART_PRECOMPILED_RUNTIME)
+#if !defined(DART_PRECOMPILED_RUNTIME)
if (optimized && FLAG_trace_inlining_intervals) {
code.DumpInlineIntervals();
}
+#endif
+
if (FLAG_trace_source_positions) {
code.DumpSourcePositions();
}
diff --git a/runtime/vm/compiler/assembler/disassembler.h b/runtime/vm/compiler/assembler/disassembler.h
index 861051d..4a86177 100644
--- a/runtime/vm/compiler/assembler/disassembler.h
+++ b/runtime/vm/compiler/assembler/disassembler.h
@@ -6,11 +6,14 @@
#define RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_H_
#include "vm/allocation.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/globals.h"
#include "vm/log.h"
#include "vm/object.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/assembler/assembler.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
// Forward declaration.
diff --git a/runtime/vm/compiler/assembler/disassembler_x86.cc b/runtime/vm/compiler/assembler/disassembler_x86.cc
index b51f464..0d162a5 100644
--- a/runtime/vm/compiler/assembler/disassembler_x86.cc
+++ b/runtime/vm/compiler/assembler/disassembler_x86.cc
@@ -11,6 +11,7 @@
#include "platform/utils.h"
#include "vm/allocation.h"
+#include "vm/constants_x86.h"
#include "vm/heap/heap.h"
#include "vm/instructions.h"
#include "vm/os.h"
diff --git a/runtime/vm/compiler/backend/constant_propagator.cc b/runtime/vm/compiler/backend/constant_propagator.cc
index 42382a8..ca953b5 100644
--- a/runtime/vm/compiler/backend/constant_propagator.cc
+++ b/runtime/vm/compiler/backend/constant_propagator.cc
@@ -38,7 +38,8 @@
non_constant_(Object::non_constant()),
constant_value_(Object::Handle(Z)),
reachable_(new (Z) BitVector(Z, graph->preorder().length())),
- marked_phis_(new (Z) BitVector(Z, graph->max_virtual_register_number())),
+ unwrapped_phis_(new (Z)
+ BitVector(Z, graph->max_virtual_register_number())),
block_worklist_(),
definition_worklist_(graph, 10) {}
@@ -220,7 +221,16 @@
// Phi value depends on the reachability of a predecessor. We have
// to revisit phis every time a predecessor becomes reachable.
for (PhiIterator it(instr->successor()); !it.Done(); it.Advance()) {
- it.Current()->Accept(this);
+ PhiInstr* phi = it.Current();
+ phi->Accept(this);
+
+ // If this phi was previously unwrapped as redundant and it is no longer
+ // redundant (does not unwrap) then we need to revisit the uses.
+ if (unwrapped_phis_->Contains(phi->ssa_temp_index()) &&
+ (UnwrapPhi(phi) == phi)) {
+ unwrapped_phis_->Remove(phi->ssa_temp_index());
+ definition_worklist_.Add(phi);
+ }
}
}
@@ -315,9 +325,25 @@
return defn;
}
-void ConstantPropagator::MarkPhi(Definition* phi) {
+void ConstantPropagator::MarkUnwrappedPhi(Definition* phi) {
ASSERT(phi->IsPhi());
- marked_phis_->Add(phi->ssa_temp_index());
+ unwrapped_phis_->Add(phi->ssa_temp_index());
+}
+
+ConstantPropagator::PhiInfo* ConstantPropagator::GetPhiInfo(PhiInstr* phi) {
+ if (phi->HasPassSpecificId(CompilerPass::kConstantPropagation)) {
+ const intptr_t id =
+ phi->GetPassSpecificId(CompilerPass::kConstantPropagation);
+ // Note: id might have been assigned by the previous round of constant
+ // propagation, so we need to verify it before using it.
+ if (id < phis_.length() && phis_[id].phi == phi) {
+ return &phis_[id];
+ }
+ }
+
+ phi->SetPassSpecificId(CompilerPass::kConstantPropagation, phis_.length());
+ phis_.Add({phi, 0});
+ return &phis_.Last();
}
// --------------------------------------------------------------------------
@@ -325,6 +351,29 @@
// and the definition has input uses, add the definition to the definition
// worklist so that the used can be processed.
void ConstantPropagator::VisitPhi(PhiInstr* instr) {
+ // Detect convergence issues by checking if visit count for this phi
+ // is too high. We should only visit this phi once for every predecessor
+ // becoming reachable, once for every input changing its constant value and
+ // once for an unwrapped redundant phi becoming non-redundant.
+ // Inputs can only change their constant value at most three times: from
+ // non-constant to unknown to specific constant to non-constant. The first
+ // link (non-constant to ...) can happen when we run the second round of
+ // constant propagation - some instructions can have non-constant assigned to
+ // them at the end of the previous constant propagation.
+ auto info = GetPhiInfo(instr);
+ info->visit_count++;
+ const intptr_t kMaxVisitsExpected = 5 * instr->InputCount();
+ if (info->visit_count > kMaxVisitsExpected) {
+ OS::PrintErr(
+ "ConstantPropagation pass is failing to converge on graph for %s\n",
+ graph_->parsed_function().function().ToCString());
+ OS::PrintErr("Phi %s was visited %" Pd " times\n", instr->ToCString(),
+ info->visit_count);
+ NOT_IN_PRODUCT(
+ FlowGraphPrinter::PrintGraph("Constant Propagation", graph_));
+ FATAL("Aborting due to non-covergence.");
+ }
+
// Compute the join over all the reachable predecessor values.
JoinEntryInstr* block = instr->block();
Object& value = Object::ZoneHandle(Z, Unknown());
@@ -334,11 +383,7 @@
Join(&value, instr->InputAt(pred_idx)->definition()->constant_value());
}
}
- if (!SetValue(instr, value) &&
- marked_phis_->Contains(instr->ssa_temp_index())) {
- marked_phis_->Remove(instr->ssa_temp_index());
- definition_worklist_.Add(instr);
- }
+ SetValue(instr, value);
}
void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) {
@@ -523,12 +568,13 @@
Definition* unwrapped_right_defn = UnwrapPhi(right_defn);
if (unwrapped_left_defn == unwrapped_right_defn) {
// Fold x === x, and x !== x to true/false.
- SetValue(instr, Bool::Get(instr->kind() == Token::kEQ_STRICT));
- if (unwrapped_left_defn != left_defn) {
- MarkPhi(left_defn);
- }
- if (unwrapped_right_defn != right_defn) {
- MarkPhi(right_defn);
+ if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ_STRICT))) {
+ if (unwrapped_left_defn != left_defn) {
+ MarkUnwrappedPhi(left_defn);
+ }
+ if (unwrapped_right_defn != right_defn) {
+ MarkUnwrappedPhi(right_defn);
+ }
}
return;
}
@@ -626,12 +672,13 @@
Definition* unwrapped_right_defn = UnwrapPhi(right_defn);
if (unwrapped_left_defn == unwrapped_right_defn) {
// Fold x === x, and x !== x to true/false.
- SetValue(instr, Bool::Get(instr->kind() == Token::kEQ));
- if (unwrapped_left_defn != left_defn) {
- MarkPhi(left_defn);
- }
- if (unwrapped_right_defn != right_defn) {
- MarkPhi(right_defn);
+ if (SetValue(instr, Bool::Get(instr->kind() == Token::kEQ))) {
+ if (unwrapped_left_defn != left_defn) {
+ MarkUnwrappedPhi(left_defn);
+ }
+ if (unwrapped_right_defn != right_defn) {
+ MarkUnwrappedPhi(right_defn);
+ }
}
return;
}
diff --git a/runtime/vm/compiler/backend/constant_propagator.h b/runtime/vm/compiler/backend/constant_propagator.h
index eda356b..dd32d16 100644
--- a/runtime/vm/compiler/backend/constant_propagator.h
+++ b/runtime/vm/compiler/backend/constant_propagator.h
@@ -45,8 +45,12 @@
void SetReachable(BlockEntryInstr* block);
bool SetValue(Definition* definition, const Object& value);
+ // Phi might be viewed as redundant based on current reachability of
+ // predecessor blocks (i.e. the same definition is flowing from all
+ // reachable predecessors). We can use this information to constant
+ // fold phi(x) == x and phi(x) != x comparisons.
Definition* UnwrapPhi(Definition* defn);
- void MarkPhi(Definition* defn);
+ void MarkUnwrappedPhi(Definition* defn);
// Assign the join (least upper bound) of a pair of abstract values to the
// first one.
@@ -67,7 +71,18 @@
#define DECLARE_VISIT(type, attrs) virtual void Visit##type(type##Instr* instr);
FOR_EACH_INSTRUCTION(DECLARE_VISIT)
+
#undef DECLARE_VISIT
+ // Structure tracking visit counts for phis. Used to detect infinite loops.
+ struct PhiInfo {
+ PhiInstr* phi;
+ intptr_t visit_count;
+ };
+
+ // Returns PhiInfo associated with the given phi. Note that this
+ // pointer can be invalidated by subsequent call to GetPhiInfo and
+ // thus should not be stored anywhere.
+ PhiInfo* GetPhiInfo(PhiInstr* phi);
Isolate* isolate() const { return graph_->isolate(); }
@@ -84,7 +99,15 @@
// preorder number.
BitVector* reachable_;
- BitVector* marked_phis_;
+ // Bitvector of phis that were "unwrapped" into one of their inputs
+ // when visiting one of their uses. These uses of these phis
+ // should be revisited if reachability of the predecessor blocks
+ // changes even if that does not change constant value of the phi.
+ BitVector* unwrapped_phis_;
+
+ // List of visited phis indexed by their id (stored as pass specific id on
+ // a phi instruction).
+ GrowableArray<PhiInfo> phis_;
// Worklists of blocks and definitions.
GrowableArray<BlockEntryInstr*> block_worklist_;
diff --git a/runtime/vm/compiler/backend/constant_propagator_test.cc b/runtime/vm/compiler/backend/constant_propagator_test.cc
new file mode 100644
index 0000000..40db6b4
--- /dev/null
+++ b/runtime/vm/compiler/backend/constant_propagator_test.cc
@@ -0,0 +1,94 @@
+// 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.
+
+#include "vm/compiler/backend/constant_propagator.h"
+
+#include "vm/compiler/backend/block_builder.h"
+#include "vm/compiler/backend/il_test_helper.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+// Test issue https://github.com/flutter/flutter/issues/53903.
+//
+// If graph contains a cyclic phi which participates in an EqualityCompare
+// or StrictCompare with its input like phi(x, ...) == x then constant
+// propagation might fail to converge by constantly revisiting this phi and
+// its uses (which includes comparison and the phi itself).
+ISOLATE_UNIT_TEST_CASE(ConstantPropagation_PhiUnwrappingAndConvergence) {
+ using compiler::BlockBuilder;
+ CompilerState S(thread, /*is_aot=*/false);
+ FlowGraphBuilderHelper H;
+
+ // We are going to build the following graph:
+ //
+ // B0[graph_entry]
+ // B1[function_entry]:
+ // v0 <- Constant(0)
+ // goto B2
+ // B2:
+ // v1 <- phi(v0, v1)
+ // v2 <- EqualityCompare(v1 == v0)
+ // if v2 == true then B4 else B3
+ // B3:
+ // goto B2
+ // B4:
+ // Return(v1)
+
+ PhiInstr* v1;
+ ConstantInstr* v0 = H.IntConstant(0);
+ auto b1 = H.flow_graph()->graph_entry()->normal_entry();
+ auto b2 = H.JoinEntry();
+ auto b3 = H.TargetEntry();
+ auto b4 = H.TargetEntry();
+
+ {
+ BlockBuilder builder(H.flow_graph(), b1);
+ builder.AddInstruction(new GotoInstr(b2, S.GetNextDeoptId()));
+ }
+
+ {
+ BlockBuilder builder(H.flow_graph(), b2);
+ v1 = H.Phi(b2, {{b1, v0}, {b3, FlowGraphBuilderHelper::kPhiSelfReference}});
+ builder.AddPhi(v1);
+ auto v2 = builder.AddDefinition(new EqualityCompareInstr(
+ TokenPosition::kNoSource, Token::kEQ, new Value(v1), new Value(v0),
+ kSmiCid, S.GetNextDeoptId()));
+ builder.AddBranch(
+ new StrictCompareInstr(
+ TokenPosition::kNoSource, Token::kEQ_STRICT, new Value(v2),
+ new Value(H.flow_graph()->GetConstant(Bool::True())),
+ /*needs_number_check=*/false, S.GetNextDeoptId()),
+ b4, b3);
+ }
+
+ {
+ BlockBuilder builder(H.flow_graph(), b3);
+ builder.AddInstruction(new GotoInstr(b2, S.GetNextDeoptId()));
+ }
+
+ {
+ BlockBuilder builder(H.flow_graph(), b4);
+ builder.AddReturn(new Value(v1));
+ }
+
+ H.FinishGraph();
+
+ // Graph transformations will attempt to copy deopt information from
+ // branches and block entries which we did not assign.
+ // To disable copying we mark graph to disable LICM.
+ H.flow_graph()->disallow_licm();
+
+ ConstantPropagator::Optimize(H.flow_graph());
+
+ auto& blocks = H.flow_graph()->reverse_postorder();
+ EXPECT_EQ(2, blocks.length());
+ EXPECT_PROPERTY(blocks[0], it.IsGraphEntry());
+ EXPECT_PROPERTY(blocks[1], it.IsFunctionEntry());
+ EXPECT_PROPERTY(blocks[1]->next(), it.IsReturn());
+ EXPECT_PROPERTY(blocks[1]->next()->AsReturn(),
+ it.value()->definition() == v0);
+}
+
+} // namespace dart
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 92c0a64..f9f22ba 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -761,7 +761,7 @@
// the pool index at runtime. There is therefore no reason to put the name
// into the pool in the first place.
// TODO(dartbug.com/40605): Move this info to the pc descriptors.
- if (!FLAG_dwarf_stack_traces) {
+ if (!FLAG_dwarf_stack_traces_mode) {
const intptr_t name_index =
assembler()->object_pool_builder().FindObject(name);
code_source_map_builder_->NoteNullCheck(assembler()->CodeSize(), token_pos,
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
index 2a45dcb..2d5388c 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
@@ -7,6 +7,7 @@
#include "vm/compiler/backend/flow_graph_compiler.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/jit/compiler.h"
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
index 353cc31..051740f 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
@@ -7,6 +7,7 @@
#include "vm/compiler/backend/flow_graph_compiler.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/jit/compiler.h"
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index 086ea4d..d982292 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -8,6 +8,7 @@
#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/code_patcher.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/frontend/flow_graph_builder.h"
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index e9e7b22..cade733 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -7,6 +7,7 @@
#include "vm/compiler/backend/flow_graph_compiler.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/ffi/native_location.h"
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index c727de1..e635815 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -13,6 +13,7 @@
#include "vm/compiler/backend/compile_type.h"
#include "vm/compiler/backend/locations.h"
#include "vm/compiler/backend/slot.h"
+#include "vm/compiler/compiler_pass.h"
#include "vm/compiler/compiler_state.h"
#include "vm/compiler/ffi/marshaller.h"
#include "vm/compiler/ffi/native_calling_convention.h"
@@ -768,7 +769,6 @@
explicit Instruction(intptr_t deopt_id = DeoptId::kNone)
: deopt_id_(deopt_id),
- lifetime_position_(kNoPlaceId),
previous_(NULL),
next_(NULL),
env_(NULL),
@@ -963,8 +963,21 @@
void RemoveEnvironment();
void ReplaceInEnvironment(Definition* current, Definition* replacement);
- intptr_t lifetime_position() const { return lifetime_position_; }
- void set_lifetime_position(intptr_t pos) { lifetime_position_ = pos; }
+ // Different compiler passes can assign pass specific ids to the instruction.
+ // Only one id can be stored at a time.
+ intptr_t GetPassSpecificId(CompilerPass::Id pass) const {
+ return (PassSpecificId::DecodePass(pass_specific_id_) == pass)
+ ? PassSpecificId::DecodeId(pass_specific_id_)
+ : PassSpecificId::kNoId;
+ }
+ void SetPassSpecificId(CompilerPass::Id pass, intptr_t id) {
+ pass_specific_id_ = PassSpecificId::Encode(pass, id);
+ }
+ bool HasPassSpecificId(CompilerPass::Id pass) const {
+ return (PassSpecificId::DecodePass(pass_specific_id_) == pass) &&
+ (PassSpecificId::DecodeId(pass_specific_id_) !=
+ PassSpecificId::kNoId);
+ }
bool HasUnmatchedInputRepresentations() const;
@@ -1039,11 +1052,6 @@
// Get the block entry for this instruction.
virtual BlockEntryInstr* GetBlock();
- // Place identifiers used by the load optimization pass.
- intptr_t place_id() const { return place_id_; }
- void set_place_id(intptr_t place_id) { place_id_ = place_id; }
- bool HasPlaceId() const { return place_id_ != kNoPlaceId; }
-
intptr_t inlining_id() const { return inlining_id_; }
void set_inlining_id(intptr_t value) {
ASSERT(value >= 0);
@@ -1141,13 +1149,28 @@
virtual void RawSetInputAt(intptr_t i, Value* value) = 0;
- enum { kNoPlaceId = -1 };
+ class PassSpecificId {
+ public:
+ static intptr_t Encode(CompilerPass::Id pass, intptr_t id) {
+ return (id << kPassBits) | pass;
+ }
+
+ static CompilerPass::Id DecodePass(intptr_t value) {
+ return static_cast<CompilerPass::Id>(value & Utils::NBitMask(kPassBits));
+ }
+
+ static intptr_t DecodeId(intptr_t value) { return (value >> kPassBits); }
+
+ static constexpr intptr_t kNoId = -1;
+
+ private:
+ static constexpr intptr_t kPassBits = 8;
+ static_assert(CompilerPass::kNumPasses <= (1 << kPassBits),
+ "Pass Id does not fit into the bit field");
+ };
intptr_t deopt_id_;
- union {
- intptr_t lifetime_position_; // Position used by register allocator.
- intptr_t place_id_;
- };
+ intptr_t pass_specific_id_ = PassSpecificId::kNoId;
Instruction* previous_;
Instruction* next_;
Environment* env_;
@@ -5804,13 +5827,8 @@
virtual bool HasUnknownSideEffects() const { return false; }
virtual bool WillAllocateNewOrRemembered() const {
- return WillAllocateNewOrRemembered(num_context_variables_);
- }
-
- static bool WillAllocateNewOrRemembered(intptr_t num_context_variables) {
- if (!Context::IsValidLength(num_context_variables)) return false;
- return Heap::IsAllocatableInNewSpace(
- Context::InstanceSize(num_context_variables));
+ return compiler::target::WillAllocateNewOrRememberedContext(
+ num_context_variables_);
}
virtual AliasIdentity Identity() const { return identity_; }
@@ -5965,12 +5983,8 @@
if (!num_elements()->BindsToConstant()) return false;
const Object& length = num_elements()->BoundConstant();
if (!length.IsSmi()) return false;
- return WillAllocateNewOrRemembered(Smi::Cast(length).Value());
- }
-
- static bool WillAllocateNewOrRemembered(const intptr_t length) {
- if (!Array::IsValidLength(length)) return false;
- return !Array::UseCardMarkingForAllocation(length);
+ return compiler::target::WillAllocateNewOrRememberedArray(
+ Smi::Cast(length).Value());
}
private:
@@ -6291,13 +6305,8 @@
virtual bool HasUnknownSideEffects() const { return false; }
virtual bool WillAllocateNewOrRemembered() const {
- return WillAllocateNewOrRemembered(context_slots().length());
- }
-
- static bool WillAllocateNewOrRemembered(intptr_t num_context_variables) {
- if (!Context::IsValidLength(num_context_variables)) return false;
- return Heap::IsAllocatableInNewSpace(
- Context::InstanceSize(num_context_variables));
+ return compiler::target::WillAllocateNewOrRememberedContext(
+ context_slots().length());
}
PRINT_OPERANDS_TO_SUPPORT
diff --git a/runtime/vm/compiler/backend/il_deserializer.cc b/runtime/vm/compiler/backend/il_deserializer.cc
index 9901da4..c8d0916 100644
--- a/runtime/vm/compiler/backend/il_deserializer.cc
+++ b/runtime/vm/compiler/backend/il_deserializer.cc
@@ -800,10 +800,6 @@
if (inst == nullptr) return nullptr;
if (env != nullptr) env->DeepCopyTo(zone(), inst);
- if (auto const lifetime_sexp =
- CheckInteger(list->ExtraLookupValue("lifetime_position"))) {
- inst->set_lifetime_position(lifetime_sexp->value());
- }
return inst;
}
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index 7903b2e..e036600 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -4,7 +4,9 @@
#include "vm/compiler/backend/il_printer.h"
+#include "vm/compiler/api/print_filter.h"
#include "vm/compiler/backend/il.h"
+#include "vm/compiler/backend/linearscan.h"
#include "vm/compiler/backend/range_analysis.h"
#include "vm/compiler/ffi/native_calling_convention.h"
#include "vm/os.h"
@@ -14,60 +16,6 @@
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
-DEFINE_FLAG(charp,
- print_flow_graph_filter,
- NULL,
- "Print only IR of functions with matching names");
-
-// Checks whether function's name matches the given filter, which is
-// a comma-separated list of strings.
-bool FlowGraphPrinter::PassesFilter(const char* filter,
- const Function& function) {
- if (filter == NULL) {
- return true;
- }
-
- char* save_ptr; // Needed for strtok_r.
- const char* scrubbed_name =
- String::Handle(function.QualifiedScrubbedName()).ToCString();
- const char* function_name = function.ToFullyQualifiedCString();
- intptr_t function_name_len = strlen(function_name);
-
- intptr_t len = strlen(filter) + 1; // Length with \0.
- char* filter_buffer = new char[len];
- strncpy(filter_buffer, filter, len); // strtok modifies arg 1.
- char* token = strtok_r(filter_buffer, ",", &save_ptr);
- bool found = false;
- while (token != NULL) {
- if ((strstr(function_name, token) != NULL) ||
- (strstr(scrubbed_name, token) != NULL)) {
- found = true;
- break;
- }
- const intptr_t token_len = strlen(token);
- if (token[token_len - 1] == '%') {
- if (function_name_len > token_len) {
- const char* suffix =
- function_name + (function_name_len - token_len + 1);
- if (strncmp(suffix, token, token_len - 1) == 0) {
- found = true;
- break;
- }
- }
- }
- token = strtok_r(NULL, ",", &save_ptr);
- }
- delete[] filter_buffer;
-
- return found;
-}
-
-bool FlowGraphPrinter::ShouldPrint(const Function& function) {
- return PassesFilter(FLAG_print_flow_graph_filter, function);
-}
-
-#if !defined(DART_PRECOMPILED_RUNTIME)
-
DEFINE_FLAG(bool,
display_sorted_ic_data,
false,
@@ -76,6 +24,10 @@
DECLARE_FLAG(bool, trace_inlining_intervals);
+bool FlowGraphPrinter::ShouldPrint(const Function& function) {
+ return compiler::PrintFilter::ShouldPrint(function);
+}
+
void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) {
LogBlock lb;
THR_Print("*** BEGIN CFG\n%s\n", phase);
@@ -123,8 +75,8 @@
if (print_locations && (instr->HasLocs())) {
instr->locs()->PrintTo(&f);
}
- if (instr->lifetime_position() != -1) {
- THR_Print("%3" Pd ": ", instr->lifetime_position());
+ if (FlowGraphAllocator::HasLifetimePosition(instr)) {
+ THR_Print("%3" Pd ": ", FlowGraphAllocator::GetLifetimePosition(instr));
}
if (!instr->IsBlockEntry()) THR_Print(" ");
THR_Print("%s", str);
@@ -1224,12 +1176,8 @@
return Thread::Current()->zone()->MakeCopyOfString(buffer);
}
-#endif // !defined(DART_PRECOMPILED_RUNTIME)
-
#else // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
-#if !defined(DART_PRECOMPILED_RUNTIME)
-
const char* Instruction::ToCString() const {
return DebugName();
}
@@ -1266,8 +1214,6 @@
return false;
}
-#endif // !defined(DART_PRECOMPILED_RUNTIME)
-
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
} // namespace dart
diff --git a/runtime/vm/compiler/backend/il_printer.h b/runtime/vm/compiler/backend/il_printer.h
index 32cc84a..becdf59 100644
--- a/runtime/vm/compiler/backend/il_printer.h
+++ b/runtime/vm/compiler/backend/il_printer.h
@@ -54,8 +54,6 @@
static bool ShouldPrint(const Function& function);
- static bool PassesFilter(const char* filter, const Function& function);
-
private:
const Function& function_;
const GrowableArray<BlockEntryInstr*>& block_order_;
diff --git a/runtime/vm/compiler/backend/il_serializer.cc b/runtime/vm/compiler/backend/il_serializer.cc
index f2e5cf9..bdd8812 100644
--- a/runtime/vm/compiler/backend/il_serializer.cc
+++ b/runtime/vm/compiler/backend/il_serializer.cc
@@ -835,9 +835,6 @@
if (!token_pos().IsNoSource()) {
s->AddExtraInteger(sexp, "token_pos", token_pos().value());
}
- if (lifetime_position() != kNoPlaceId) {
- s->AddExtraInteger(sexp, "lifetime_position", lifetime_position());
- }
}
SExpression* Range::ToSExpression(FlowGraphSerializer* s) {
diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h
index 9655791..5232aabb 100644
--- a/runtime/vm/compiler/backend/il_test_helper.h
+++ b/runtime/vm/compiler/backend/il_test_helper.h
@@ -253,17 +253,20 @@
return flow_graph_.GetConstant(Double::Handle(Double::NewCanonical(value)));
}
+ static constexpr Definition* kPhiSelfReference = nullptr;
+
PhiInstr* Phi(JoinEntryInstr* join,
std::initializer_list<std::pair<BlockEntryInstr*, Definition*>>
- incomming) {
- auto phi = new PhiInstr(join, incomming.size());
- for (size_t i = 0; i < incomming.size(); i++) {
+ incoming) {
+ auto phi = new PhiInstr(join, incoming.size());
+ for (size_t i = 0; i < incoming.size(); i++) {
auto input = new Value(flow_graph_.constant_dead());
phi->SetInputAt(i, input);
input->definition()->AddInputUse(input);
}
- for (auto pair : incomming) {
- pending_phis_.Add({phi, pair.first, pair.second});
+ for (auto pair : incoming) {
+ pending_phis_.Add({phi, pair.first,
+ pair.second == kPhiSelfReference ? phi : pair.second});
}
return phi;
}
diff --git a/runtime/vm/compiler/backend/linearscan.cc b/runtime/vm/compiler/backend/linearscan.cc
index 402954d..9bcd928 100644
--- a/runtime/vm/compiler/backend/linearscan.cc
+++ b/runtime/vm/compiler/backend/linearscan.cc
@@ -682,15 +682,15 @@
range->set_assigned_location(loc);
AssignSafepoints(defn, range);
range->finger()->Initialize(range);
- SplitInitialDefinitionAt(range, block->lifetime_position() + 1);
+ SplitInitialDefinitionAt(range, GetLifetimePosition(block) + 1);
ConvertAllUses(range);
// We have exception/stacktrace in a register and need to
// ensure this register is not available for register allocation during
// the [CatchBlockEntry] to ensure it's not overwritten.
if (loc.IsRegister()) {
- BlockLocation(loc, block->lifetime_position(),
- block->lifetime_position() + 1);
+ BlockLocation(loc, GetLifetimePosition(block),
+ GetLifetimePosition(block) + 1);
}
return;
}
@@ -745,12 +745,12 @@
range->set_assigned_location(loc);
if (loc.IsRegister()) {
AssignSafepoints(defn, range);
- if (range->End() > (block->lifetime_position() + 2)) {
- SplitInitialDefinitionAt(range, block->lifetime_position() + 2);
+ if (range->End() > (GetLifetimePosition(block) + 2)) {
+ SplitInitialDefinitionAt(range, GetLifetimePosition(block) + 2);
}
ConvertAllUses(range);
- BlockLocation(loc, block->lifetime_position(),
- block->lifetime_position() + 2);
+ BlockLocation(loc, GetLifetimePosition(block),
+ GetLifetimePosition(block) + 2);
return;
}
} else {
@@ -837,7 +837,7 @@
ParallelMoveInstr* parallel_move = goto_instr->parallel_move();
// All uses are recorded at the position of parallel move preceding goto.
- const intptr_t pos = goto_instr->lifetime_position();
+ const intptr_t pos = GetLifetimePosition(goto_instr);
JoinEntryInstr* join = goto_instr->successor();
ASSERT(join != NULL);
@@ -975,7 +975,7 @@
}
const intptr_t block_start_pos = block->start_pos();
- const intptr_t use_pos = current->lifetime_position() + 1;
+ const intptr_t use_pos = GetLifetimePosition(current) + 1;
Location* locations = flow_graph_.zone()->Alloc<Location>(env->Length());
@@ -1306,7 +1306,7 @@
}
}
- const intptr_t pos = current->lifetime_position();
+ const intptr_t pos = GetLifetimePosition(current);
ASSERT(IsInstructionStartPosition(pos));
ASSERT(locs->input_count() == current->InputCount());
@@ -1524,11 +1524,12 @@
ASSERT(pos > 0);
Instruction* prev = instr->previous();
ParallelMoveInstr* move = prev->AsParallelMove();
- if ((move == NULL) || (move->lifetime_position() != pos)) {
+ if ((move == NULL) ||
+ (FlowGraphAllocator::GetLifetimePosition(move) != pos)) {
move = new ParallelMoveInstr();
prev->LinkTo(move);
move->LinkTo(instr);
- move->set_lifetime_position(pos);
+ FlowGraphAllocator::SetLifetimePosition(move, pos);
}
return move;
}
@@ -1536,7 +1537,8 @@
static ParallelMoveInstr* CreateParallelMoveAfter(Instruction* instr,
intptr_t pos) {
Instruction* next = instr->next();
- if (next->IsParallelMove() && (next->lifetime_position() == pos)) {
+ if (next->IsParallelMove() &&
+ (FlowGraphAllocator::GetLifetimePosition(next) == pos)) {
return next->AsParallelMove();
}
return CreateParallelMoveBefore(next, pos);
@@ -1558,7 +1560,7 @@
instructions_.Add(block);
block_entries_.Add(block);
block->set_start_pos(pos);
- block->set_lifetime_position(pos);
+ SetLifetimePosition(block, pos);
pos += 2;
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
@@ -1567,7 +1569,7 @@
if (!current->IsParallelMove()) {
instructions_.Add(current);
block_entries_.Add(block);
- current->set_lifetime_position(pos);
+ SetLifetimePosition(current, pos);
pos += 2;
}
}
@@ -1832,7 +1834,7 @@
BlockEntryInstr* split_block_entry = BlockEntryAt(to);
ASSERT(split_block_entry == InstructionAt(to)->GetBlock());
- if (from < split_block_entry->lifetime_position()) {
+ if (from < GetLifetimePosition(split_block_entry)) {
// Interval [from, to) spans multiple blocks.
// If the last block is inside a loop, prefer splitting at the outermost
@@ -1863,16 +1865,16 @@
}
}
while ((loop_info != nullptr) &&
- (from < loop_info->header()->lifetime_position())) {
+ (from < GetLifetimePosition(loop_info->header()))) {
split_block_entry = loop_info->header();
loop_info = loop_info->outer();
TRACE_ALLOC(THR_Print(" move back to loop header B%" Pd " at %" Pd "\n",
split_block_entry->block_id(),
- split_block_entry->lifetime_position()));
+ GetLifetimePosition(split_block_entry)));
}
// Split at block's start.
- split_pos = split_block_entry->lifetime_position();
+ split_pos = GetLifetimePosition(split_block_entry);
} else {
// Interval [from, to) is contained inside a single block.
@@ -2587,7 +2589,7 @@
continue;
}
- const intptr_t pos = safepoint_instr->lifetime_position();
+ const intptr_t pos = GetLifetimePosition(safepoint_instr);
if (range->End() <= pos) break;
if (range->Contains(pos)) {
diff --git a/runtime/vm/compiler/backend/linearscan.h b/runtime/vm/compiler/backend/linearscan.h
index 541bd2c..4bb868b 100644
--- a/runtime/vm/compiler/backend/linearscan.h
+++ b/runtime/vm/compiler/backend/linearscan.h
@@ -63,6 +63,20 @@
// Map a virtual register number to its live range.
LiveRange* GetLiveRange(intptr_t vreg);
+ DART_FORCE_INLINE static void SetLifetimePosition(Instruction* instr,
+ intptr_t pos) {
+ instr->SetPassSpecificId(CompilerPass::kAllocateRegisters, pos);
+ }
+
+ DART_FORCE_INLINE static bool HasLifetimePosition(Instruction* instr) {
+ return instr->HasPassSpecificId(CompilerPass::kAllocateRegisters);
+ }
+
+ DART_FORCE_INLINE static intptr_t GetLifetimePosition(
+ const Instruction* instr) {
+ return instr->GetPassSpecificId(CompilerPass::kAllocateRegisters);
+ }
+
private:
void CollectRepresentations();
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 0559662..01e5c51 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1245,6 +1245,18 @@
return phi_moves;
}
+DART_FORCE_INLINE static void SetPlaceId(Instruction* instr, intptr_t id) {
+ instr->SetPassSpecificId(CompilerPass::kCSE, id);
+}
+
+DART_FORCE_INLINE static intptr_t GetPlaceId(const Instruction* instr) {
+ return instr->GetPassSpecificId(CompilerPass::kCSE);
+}
+
+DART_FORCE_INLINE static bool HasPlaceId(const Instruction* instr) {
+ return instr->HasPassSpecificId(CompilerPass::kCSE);
+}
+
enum CSEMode { kOptimizeLoads, kOptimizeStores };
static AliasedSet* NumberPlaces(
@@ -1282,7 +1294,7 @@
}
}
- instr->set_place_id(result->id());
+ SetPlaceId(instr, result->id());
}
}
@@ -1309,8 +1321,8 @@
intptr_t loop_header_index,
Instruction* instr) {
return IsLoadEliminationCandidate(instr) && (sets != NULL) &&
- instr->HasPlaceId() && ((*sets)[loop_header_index] != NULL) &&
- (*sets)[loop_header_index]->Contains(instr->place_id());
+ HasPlaceId(instr) &&
+ (*sets)[loop_header_index]->Contains(GetPlaceId(instr));
}
LICM::LICM(FlowGraph* flow_graph) : flow_graph_(flow_graph) {
@@ -1659,7 +1671,7 @@
// instruction that still points to the old place with a more
// generic alias.
const intptr_t old_alias_id = aliased_set_->LookupAliasId(
- aliased_set_->places()[instr->place_id()]->ToAlias());
+ aliased_set_->places()[GetPlaceId(instr)]->ToAlias());
killed = aliased_set_->GetKilledSet(old_alias_id);
}
@@ -1678,7 +1690,7 @@
if (FLAG_trace_optimization) {
THR_Print("Removing redundant store to place %" Pd
" in block B%" Pd "\n",
- instr->place_id(), block->block_id());
+ GetPlaceId(instr), block->block_id());
}
instr_it.RemoveCurrentFromGraph();
continue;
@@ -1711,8 +1723,8 @@
// load forwarding.
const Place* canonical = aliased_set_->LookupCanonical(&place);
if ((canonical != NULL) &&
- (canonical->id() != instr->AsDefinition()->place_id())) {
- instr->AsDefinition()->set_place_id(canonical->id());
+ (canonical->id() != GetPlaceId(instr->AsDefinition()))) {
+ SetPlaceId(instr->AsDefinition(), canonical->id());
}
}
@@ -1751,11 +1763,11 @@
intptr_t place_id = 0;
if (auto load = use->instruction()->AsLoadField()) {
slot = &load->slot();
- place_id = load->place_id();
+ place_id = GetPlaceId(load);
} else if (auto store =
use->instruction()->AsStoreInstanceField()) {
slot = &store->slot();
- place_id = store->place_id();
+ place_id = GetPlaceId(store);
}
if (slot != nullptr) {
@@ -1789,7 +1801,7 @@
continue;
}
- const intptr_t place_id = defn->place_id();
+ const intptr_t place_id = GetPlaceId(defn);
if (gen->Contains(place_id)) {
// This is a locally redundant load.
ASSERT((out_values != NULL) && ((*out_values)[place_id] != NULL));
@@ -1965,7 +1977,7 @@
(in_[preorder_number]->Contains(place_id))) {
PhiInstr* phi = new (Z)
PhiInstr(block->AsJoinEntry(), block->PredecessorCount());
- phi->set_place_id(place_id);
+ SetPlaceId(phi, place_id);
pending_phis.Add(phi);
in_value = phi;
}
@@ -2103,14 +2115,14 @@
// Incoming values are different. Phi is required to merge.
PhiInstr* phi =
new (Z) PhiInstr(block->AsJoinEntry(), block->PredecessorCount());
- phi->set_place_id(place_id);
+ SetPlaceId(phi, place_id);
FillPhiInputs(phi);
return phi;
}
void FillPhiInputs(PhiInstr* phi) {
BlockEntryInstr* block = phi->GetBlock();
- const intptr_t place_id = phi->place_id();
+ const intptr_t place_id = GetPlaceId(phi);
for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
BlockEntryInstr* pred = block->PredecessorAt(i);
@@ -2154,9 +2166,9 @@
for (intptr_t i = 0; i < loads->length(); i++) {
Definition* load = (*loads)[i];
- if (!in->Contains(load->place_id())) continue; // No incoming value.
+ if (!in->Contains(GetPlaceId(load))) continue; // No incoming value.
- Definition* replacement = MergeIncomingValues(block, load->place_id());
+ Definition* replacement = MergeIncomingValues(block, GetPlaceId(load));
ASSERT(replacement != NULL);
// Sets of outgoing values are not linked into use lists so
@@ -2606,17 +2618,17 @@
// Handle stores.
if (is_store) {
- if (kill->Contains(instr->place_id())) {
- if (!live_in->Contains(instr->place_id()) &&
+ if (kill->Contains(GetPlaceId(instr))) {
+ if (!live_in->Contains(GetPlaceId(instr)) &&
CanEliminateStore(instr)) {
if (FLAG_trace_optimization) {
THR_Print("Removing dead store to place %" Pd " in block B%" Pd
"\n",
- instr->place_id(), block->block_id());
+ GetPlaceId(instr), block->block_id());
}
instr_it.RemoveCurrentFromGraph();
}
- } else if (!live_in->Contains(instr->place_id())) {
+ } else if (!live_in->Contains(GetPlaceId(instr))) {
// Mark this store as down-ward exposed: They are the only
// candidates for the global store elimination.
if (exposed_stores == NULL) {
@@ -2628,8 +2640,8 @@
exposed_stores->Add(instr);
}
// Interfering stores kill only loads from the same place.
- kill->Add(instr->place_id());
- live_in->Remove(instr->place_id());
+ kill->Add(GetPlaceId(instr));
+ live_in->Remove(GetPlaceId(instr));
continue;
}
@@ -2690,11 +2702,11 @@
}
// Eliminate a downward exposed store if the corresponding place is not
// in live-out.
- if (!live_out->Contains(instr->place_id()) &&
+ if (!live_out->Contains(GetPlaceId(instr)) &&
CanEliminateStore(instr)) {
if (FLAG_trace_optimization) {
THR_Print("Removing dead store to place %" Pd " block B%" Pd "\n",
- instr->place_id(), block->block_id());
+ GetPlaceId(instr), block->block_id());
}
instr->RemoveFromGraph(/* ignored */ false);
}
@@ -3316,6 +3328,18 @@
EliminateDeadParameters();
}
+ static intptr_t GetParameterId(const Instruction* instr) {
+ return instr->GetPassSpecificId(CompilerPass::kTryCatchOptimization);
+ }
+
+ static void SetParameterId(Instruction* instr, intptr_t id) {
+ instr->SetPassSpecificId(CompilerPass::kTryCatchOptimization, id);
+ }
+
+ static bool HasParameterId(Instruction* instr) {
+ return instr->HasPassSpecificId(CompilerPass::kTryCatchOptimization);
+ }
+
// Assign sequential ids to each ParameterInstr in each CatchEntryBlock.
// Collect reverse mapping from try indexes to corresponding catches.
void NumberCatchEntryParameters() {
@@ -3324,7 +3348,7 @@
*catch_entry->initial_definitions();
for (auto idef : idefs) {
if (idef->IsParameter()) {
- idef->set_place_id(parameter_info_.length());
+ SetParameterId(idef, parameter_info_.length());
parameter_info_.Add(new ParameterInfo(idef->AsParameter()));
}
}
@@ -3363,14 +3387,14 @@
// already present in the list.
bool found = false;
for (auto other_defn :
- parameter_info_[param->place_id()]->incoming) {
+ parameter_info_[GetParameterId(param)]->incoming) {
if (other_defn == defn) {
found = true;
break;
}
}
if (!found) {
- parameter_info_[param->place_id()]->incoming.Add(defn);
+ parameter_info_[GetParameterId(param)]->incoming.Add(defn);
}
}
}
@@ -3410,7 +3434,7 @@
while (!worklist_.IsEmpty()) {
Definition* defn = worklist_.RemoveLast();
if (ParameterInstr* param = defn->AsParameter()) {
- auto s = parameter_info_[param->place_id()];
+ auto s = parameter_info_[GetParameterId(param)];
for (auto input : s->incoming) {
MarkLive(input);
}
@@ -3431,8 +3455,8 @@
worklist_.Add(phi);
}
} else if (ParameterInstr* param = defn->AsParameter()) {
- if (param->place_id() != -1) {
- auto input_s = parameter_info_[param->place_id()];
+ if (HasParameterId(param)) {
+ auto input_s = parameter_info_[GetParameterId(param)];
if (!input_s->alive) {
input_s->alive = true;
worklist_.Add(param);
@@ -3466,7 +3490,7 @@
for (intptr_t env_idx = 0; env_idx < idefs.length(); ++env_idx) {
if (ParameterInstr* param = idefs[env_idx]->AsParameter()) {
- if (!parameter_info_[param->place_id()]->alive) {
+ if (!parameter_info_[GetParameterId(param)]->alive) {
env->ValueAt(env_idx)->BindToEnvironment(
flow_graph_->constant_null());
}
diff --git a/runtime/vm/compiler/compiler_pass.h b/runtime/vm/compiler/compiler_pass.h
index f3333b91..a5d4cdd 100644
--- a/runtime/vm/compiler/compiler_pass.h
+++ b/runtime/vm/compiler/compiler_pass.h
@@ -115,7 +115,7 @@
};
#define ADD_ONE(name) +1
- static const intptr_t kNumPasses = 0 COMPILER_PASS_LIST(ADD_ONE);
+ static constexpr intptr_t kNumPasses = 0 COMPILER_PASS_LIST(ADD_ONE);
#undef ADD_ONE
CompilerPass(Id id, const char* name) : name_(name), flags_(0) {
diff --git a/runtime/vm/compiler/compiler_sources.gni b/runtime/vm/compiler/compiler_sources.gni
index fe2e471..6b59f94 100644
--- a/runtime/vm/compiler/compiler_sources.gni
+++ b/runtime/vm/compiler/compiler_sources.gni
@@ -28,13 +28,6 @@
"assembler/assembler_ia32.h",
"assembler/assembler_x64.cc",
"assembler/assembler_x64.h",
- "assembler/disassembler.cc",
- "assembler/disassembler.h",
- "assembler/disassembler_arm.cc",
- "assembler/disassembler_arm64.cc",
- "assembler/disassembler_kbc.cc",
- "assembler/disassembler_kbc.h",
- "assembler/disassembler_x86.cc",
"assembler/object_pool_builder.h",
"backend/block_builder.h",
"backend/block_scheduler.cc",
@@ -112,8 +105,6 @@
"ffi/native_calling_convention.h",
"ffi/native_location.cc",
"ffi/native_location.h",
- "ffi/native_type.cc",
- "ffi/native_type.h",
"ffi/recognized_method.cc",
"ffi/recognized_method.h",
"frontend/base_flow_graph_builder.cc",
@@ -150,8 +141,6 @@
"graph_intrinsifier_x64.cc",
"intrinsifier.cc",
"intrinsifier.h",
- "jit/compiler.cc",
- "jit/compiler.h",
"jit/jit_call_specializer.cc",
"jit/jit_call_specializer.h",
"method_recognizer.cc",
@@ -159,8 +148,6 @@
"recognized_methods_list.h",
"relocation.cc",
"relocation.h",
- "runtime_api.cc",
- "runtime_api.h",
"stub_code_compiler.cc",
"stub_code_compiler.h",
"stub_code_compiler_arm.cc",
@@ -179,6 +166,7 @@
"assembler/assembler_x64_test.cc",
"assembler/disassembler_test.cc",
"backend/bce_test.cc",
+ "backend/constant_propagator_test.cc",
"backend/il_test.cc",
"backend/il_test_helper.h",
"backend/il_test_helper.cc",
@@ -196,3 +184,26 @@
"cha_test.cc",
"write_barrier_elimination_test.cc",
]
+
+compiler_api_sources = [
+ "api/deopt_id.h",
+ "api/print_filter.cc",
+ "api/print_filter.h",
+ "api/type_check_mode.h",
+ "ffi/native_type.cc",
+ "ffi/native_type.h",
+ "jit/compiler.cc",
+ "jit/compiler.h",
+ "runtime_api.cc",
+ "runtime_api.h",
+]
+
+disassembler_sources = [
+ "assembler/disassembler.cc",
+ "assembler/disassembler.h",
+ "assembler/disassembler_arm.cc",
+ "assembler/disassembler_arm64.cc",
+ "assembler/disassembler_kbc.cc",
+ "assembler/disassembler_kbc.h",
+ "assembler/disassembler_x86.cc",
+]
diff --git a/runtime/vm/compiler/compiler_state.cc b/runtime/vm/compiler/compiler_state.cc
index 47407f5..cdf77e6 100644
--- a/runtime/vm/compiler/compiler_state.cc
+++ b/runtime/vm/compiler/compiler_state.cc
@@ -3,12 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
#include "vm/compiler/compiler_state.h"
-#include "vm/growable_array.h"
-
-#ifndef DART_PRECOMPILED_RUNTIME
#include <functional>
+#include "vm/compiler/backend/slot.h"
+#include "vm/growable_array.h"
#include "vm/scopes.h"
namespace dart {
@@ -72,5 +71,3 @@
}
} // namespace dart
-
-#endif // DART_PRECOMPILED_RUNTIME
diff --git a/runtime/vm/compiler/compiler_state.h b/runtime/vm/compiler/compiler_state.h
index 4b3a590..619f25b 100644
--- a/runtime/vm/compiler/compiler_state.h
+++ b/runtime/vm/compiler/compiler_state.h
@@ -5,6 +5,7 @@
#ifndef RUNTIME_VM_COMPILER_COMPILER_STATE_H_
#define RUNTIME_VM_COMPILER_COMPILER_STATE_H_
+#include "vm/compiler/api/deopt_id.h"
#include "vm/compiler/cha.h"
#include "vm/heap/safepoint.h"
#include "vm/thread.h"
@@ -16,44 +17,6 @@
class SlotCache;
class Slot;
-// Deoptimization Id logic.
-//
-// Deoptimization ids are used to refer to deoptimization points, at which
-// control can enter unoptimized code from the optimized version of the code.
-//
-// Note: any instruction that does a call has two deoptimization points,
-// one before the call and one after the call - so that we could deoptimize
-// to either before or after the call depending on whether the same call
-// already occured in the optimized code (and potentially produced
-// observable side-effects) or not.
-//
-// To simplify implementation we always allocate two deopt ids (one for before
-// point and one for the after point).
-class DeoptId : public AllStatic {
- public:
- static constexpr intptr_t kNone = -1;
-
- static inline intptr_t Next(intptr_t deopt_id) { return deopt_id + kStep; }
-
- static inline intptr_t ToDeoptAfter(intptr_t deopt_id) {
- ASSERT(IsDeoptBefore(deopt_id));
- return deopt_id + kAfterOffset;
- }
-
- static inline bool IsDeoptBefore(intptr_t deopt_id) {
- return (deopt_id % kStep) == kBeforeOffset;
- }
-
- static inline bool IsDeoptAfter(intptr_t deopt_id) {
- return (deopt_id % kStep) == kAfterOffset;
- }
-
- private:
- static constexpr intptr_t kStep = 2;
- static constexpr intptr_t kBeforeOffset = 0;
- static constexpr intptr_t kAfterOffset = 1;
-};
-
// Global compiler state attached to the thread.
class CompilerState : public ThreadStackResource {
public:
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index e69380c..adcc57a 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -6,10 +6,13 @@
#include "platform/assert.h"
#include "platform/globals.h"
-#include "vm/compiler/backend/locations.h"
#include "vm/compiler/runtime_api.h"
#include "vm/object.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/backend/locations.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
namespace compiler {
@@ -121,6 +124,7 @@
UNREACHABLE();
}
+#if !defined(DART_PRECOMPILED_RUNTIME)
bool NativeFundamentalType::IsExpressibleAsRepresentation() const {
switch (representation_) {
case kInt8:
@@ -163,6 +167,7 @@
UNREACHABLE();
}
}
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
bool NativeFundamentalType::Equals(const NativeType& other) const {
if (!other.IsFundamental()) {
@@ -242,6 +247,7 @@
return NativeType::FromTypedDataClassId(type.type_class_id(), zone);
}
+#if !defined(DART_PRECOMPILED_RUNTIME)
static FundamentalType fundamental_rep(Representation rep) {
switch (rep) {
case kUnboxedDouble:
@@ -264,6 +270,7 @@
Zone* zone) {
return *new (zone) NativeFundamentalType(fundamental_rep(rep));
}
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
const char* NativeType::ToCString() const {
char buffer[1024];
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index ddd6941..0eee4de 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -9,11 +9,16 @@
#include "platform/assert.h"
#include "vm/allocation.h"
-#include "vm/compiler/backend/locations.h"
#include "vm/compiler/runtime_api.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/backend/locations.h"
+#endif
+
namespace dart {
+class BufferFormatter;
+
namespace compiler {
namespace ffi {
@@ -31,7 +36,7 @@
// * tagged
// * untagged
//
-// Instead, NativeTypes support representations not supprted in Dart's unboxed
+// Instead, NativeTypes support representations not supported in Dart's unboxed
// Representations, such as:
// * Fundamental types (https://en.cppreference.com/w/cpp/language/types):
// * int8_t
@@ -48,8 +53,11 @@
public:
static NativeType& FromAbstractType(const AbstractType& type, Zone* zone);
static NativeType& FromTypedDataClassId(classid_t class_id, Zone* zone);
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
static NativeFundamentalType& FromUnboxedRepresentation(Representation rep,
Zone* zone);
+#endif
virtual bool IsFundamental() const { return false; }
const NativeFundamentalType& AsFundamental() const;
@@ -71,6 +79,7 @@
// The alignment in bytes of this representation as member of a composite.
virtual intptr_t AlignmentInBytesField() const = 0;
+#if !defined(DART_PRECOMPILED_RUNTIME)
// NativeTypes which are available as unboxed Representations.
virtual bool IsExpressibleAsRepresentation() const { return false; }
@@ -82,6 +91,7 @@
const auto& widened = WidenTo4Bytes(zone_);
return widened.AsRepresentation();
}
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
virtual bool Equals(const NativeType& other) const { UNREACHABLE(); }
@@ -135,8 +145,10 @@
virtual intptr_t AlignmentInBytesStack() const;
virtual intptr_t AlignmentInBytesField() const;
+#if !defined(DART_PRECOMPILED_RUNTIME)
virtual bool IsExpressibleAsRepresentation() const;
virtual Representation AsRepresentation() const;
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
virtual bool Equals(const NativeType& other) const;
virtual NativeFundamentalType& Split(intptr_t part, Zone* zone) const;
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index ea9e245..c8a3bf3 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -4,9 +4,9 @@
#include "vm/compiler/jit/compiler.h"
-#include "vm/compiler/assembler/assembler.h"
-
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/code_patcher.h"
+#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/compiler/backend/block_scheduler.h"
#include "vm/compiler/backend/branch_optimizer.h"
@@ -45,6 +45,7 @@
#include "vm/thread_registry.h"
#include "vm/timeline.h"
#include "vm/timer.h"
+#endif
namespace dart {
diff --git a/runtime/vm/compiler/jit/compiler.h b/runtime/vm/compiler/jit/compiler.h
index a810124..b859021 100644
--- a/runtime/vm/compiler/jit/compiler.h
+++ b/runtime/vm/compiler/jit/compiler.h
@@ -6,7 +6,7 @@
#define RUNTIME_VM_COMPILER_JIT_COMPILER_H_
#include "vm/allocation.h"
-#include "vm/compiler/compiler_state.h"
+#include "vm/compiler/api/deopt_id.h"
#include "vm/growable_array.h"
#include "vm/runtime_entry.h"
#include "vm/thread_pool.h"
diff --git a/runtime/vm/compiler/relocation.cc b/runtime/vm/compiler/relocation.cc
index 2f56864..3835d55 100644
--- a/runtime/vm/compiler/relocation.cc
+++ b/runtime/vm/compiler/relocation.cc
@@ -133,17 +133,12 @@
}
num_calls++;
- target_ = call.Get<Code::kSCallTableFunctionTarget>();
- if (target_.IsFunction()) {
- auto& fun = Function::Cast(target_);
- ASSERT(fun.HasCode());
- destination_ = fun.CurrentCode();
- ASSERT(!destination_.IsStubCode());
- } else {
- target_ = call.Get<Code::kSCallTableCodeTarget>();
- ASSERT(target_.IsCode());
- destination_ = Code::Cast(target_).raw();
- }
+ // The precompiler should have already replaced all function entries
+ // with code entries.
+ ASSERT(call.Get<Code::kSCallTableFunctionTarget>() == Function::null());
+ target_ = call.Get<Code::kSCallTableCodeTarget>();
+ ASSERT(target_.IsCode());
+ destination_ = Code::Cast(target_).raw();
// A call site can decide to jump not to the beginning of a function but
// rather jump into it at a certain (positive) offset.
@@ -256,17 +251,12 @@
continue;
}
- target_ = call.Get<Code::kSCallTableFunctionTarget>();
- if (target_.IsFunction()) {
- auto& fun = Function::Cast(target_);
- ASSERT(fun.HasCode());
- destination_ = fun.CurrentCode();
- ASSERT(!destination_.IsStubCode());
- } else {
- target_ = call.Get<Code::kSCallTableCodeTarget>();
- ASSERT(target_.IsCode());
- destination_ = Code::Cast(target_).raw();
- }
+ // The precompiler should have already replaced all function entries
+ // with code entries.
+ ASSERT(call.Get<Code::kSCallTableFunctionTarget>() == Function::null());
+ target_ = call.Get<Code::kSCallTableCodeTarget>();
+ ASSERT(target_.IsCode());
+ destination_ = Code::Cast(target_).raw();
// A call site can decide to jump not to the beginning of a function but
// rather jump into it at a certain offset.
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index 7f92b3b..4f6221e 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -4,6 +4,20 @@
#include "vm/compiler/runtime_api.h"
+#include "vm/object.h"
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/runtime_offsets_list.h"
+#include "vm/dart_entry.h"
+#include "vm/longjump.h"
+#include "vm/native_arguments.h"
+#include "vm/native_entry.h"
+#include "vm/object_store.h"
+#include "vm/runtime_entry.h"
+#include "vm/symbols.h"
+#include "vm/timeline.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
namespace compiler {
namespace target {
@@ -14,23 +28,23 @@
return Utils::IsInt(kSmiBits + 1, v);
}
+bool WillAllocateNewOrRememberedContext(intptr_t num_context_variables) {
+ if (!dart::Context::IsValidLength(num_context_variables)) return false;
+ return dart::Heap::IsAllocatableInNewSpace(
+ dart::Context::InstanceSize(num_context_variables));
+}
+
+bool WillAllocateNewOrRememberedArray(intptr_t length) {
+ if (!dart::Array::IsValidLength(length)) return false;
+ return !dart::Array::UseCardMarkingForAllocation(length);
+}
+
} // namespace target
} // namespace compiler
} // namespace dart
#if !defined(DART_PRECOMPILED_RUNTIME)
-#include "vm/compiler/runtime_offsets_list.h"
-#include "vm/dart_entry.h"
-#include "vm/longjump.h"
-#include "vm/native_arguments.h"
-#include "vm/native_entry.h"
-#include "vm/object.h"
-#include "vm/object_store.h"
-#include "vm/runtime_entry.h"
-#include "vm/symbols.h"
-#include "vm/timeline.h"
-
namespace dart {
namespace compiler {
diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h
index 5966425..c7f3303 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -362,6 +362,10 @@
word ToRawPointer(const dart::Object& a);
#endif // defined(TARGET_ARCH_IA32)
+bool WillAllocateNewOrRememberedContext(intptr_t num_context_variables);
+
+bool WillAllocateNewOrRememberedArray(intptr_t length);
+
//
// Target specific offsets and constants.
//
@@ -1071,11 +1075,12 @@
class Isolate : public AllStatic {
public:
- static word object_store_offset();
+ static word cached_object_store_offset();
static word default_tag_offset();
static word current_tag_offset();
static word user_tag_offset();
- static word class_table_offset();
+ static word cached_class_table_table_offset();
+ static word shared_class_table_offset();
static word ic_miss_code_offset();
#if !defined(PRODUCT)
static word single_step_offset();
@@ -1089,8 +1094,6 @@
class ClassTable : public AllStatic {
public:
- static word table_offset();
- static word shared_class_table_offset();
#if !defined(PRODUCT)
static word ClassOffsetFor(intptr_t cid);
static word SharedTableOffsetFor();
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 295a04b..b956285 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -81,9 +81,6 @@
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 104;
static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 16;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 8;
-static constexpr dart::compiler::target::word
SharedClassTable_class_heap_stats_table_offset = 0;
static constexpr dart::compiler::target::word Closure_context_offset = 20;
static constexpr dart::compiler::target::word
@@ -137,12 +134,16 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 16;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 40;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 20;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 24;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 32;
-static constexpr dart::compiler::target::word Isolate_single_step_offset = 60;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 32;
+static constexpr dart::compiler::target::word Isolate_single_step_offset = 48;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 16;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 16;
static constexpr dart::compiler::target::word
@@ -555,9 +556,6 @@
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 184;
static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 16;
-static constexpr dart::compiler::target::word
SharedClassTable_class_heap_stats_table_offset = 0;
static constexpr dart::compiler::target::word Closure_context_offset = 40;
static constexpr dart::compiler::target::word
@@ -611,12 +609,16 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 32;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 64;
-static constexpr dart::compiler::target::word Isolate_single_step_offset = 120;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 64;
+static constexpr dart::compiler::target::word Isolate_single_step_offset = 96;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 32;
static constexpr dart::compiler::target::word
@@ -1032,9 +1034,6 @@
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 104;
static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 16;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 8;
-static constexpr dart::compiler::target::word
SharedClassTable_class_heap_stats_table_offset = 0;
static constexpr dart::compiler::target::word Closure_context_offset = 20;
static constexpr dart::compiler::target::word
@@ -1088,12 +1087,16 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 16;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 40;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 20;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 24;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 32;
-static constexpr dart::compiler::target::word Isolate_single_step_offset = 60;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 32;
+static constexpr dart::compiler::target::word Isolate_single_step_offset = 48;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 16;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 16;
static constexpr dart::compiler::target::word
@@ -1503,9 +1506,6 @@
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 184;
static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 16;
-static constexpr dart::compiler::target::word
SharedClassTable_class_heap_stats_table_offset = 0;
static constexpr dart::compiler::target::word Closure_context_offset = 40;
static constexpr dart::compiler::target::word
@@ -1559,12 +1559,16 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 32;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 64;
-static constexpr dart::compiler::target::word Isolate_single_step_offset = 120;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 64;
+static constexpr dart::compiler::target::word Isolate_single_step_offset = 96;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 32;
static constexpr dart::compiler::target::word
@@ -1982,9 +1986,6 @@
static constexpr dart::compiler::target::word Class_super_type_offset = 44;
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 104;
-static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 16;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 8;
static constexpr dart::compiler::target::word Closure_context_offset = 20;
static constexpr dart::compiler::target::word
Closure_delayed_type_arguments_offset = 12;
@@ -2037,11 +2038,15 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 16;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 40;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 20;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 24;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 32;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 32;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 16;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 16;
static constexpr dart::compiler::target::word
@@ -2450,9 +2455,6 @@
static constexpr dart::compiler::target::word Class_super_type_offset = 88;
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 184;
-static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 16;
static constexpr dart::compiler::target::word Closure_context_offset = 40;
static constexpr dart::compiler::target::word
Closure_delayed_type_arguments_offset = 24;
@@ -2505,11 +2507,15 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 32;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 64;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 64;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 32;
static constexpr dart::compiler::target::word
@@ -2921,9 +2927,6 @@
static constexpr dart::compiler::target::word Class_super_type_offset = 44;
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 104;
-static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 16;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 8;
static constexpr dart::compiler::target::word Closure_context_offset = 20;
static constexpr dart::compiler::target::word
Closure_delayed_type_arguments_offset = 12;
@@ -2976,11 +2979,15 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 16;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 40;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 20;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 24;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 32;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 32;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 16;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 16;
static constexpr dart::compiler::target::word
@@ -3386,9 +3393,6 @@
static constexpr dart::compiler::target::word Class_super_type_offset = 88;
static constexpr dart::compiler::target::word
Class_host_type_arguments_field_offset_in_words_offset = 184;
-static constexpr dart::compiler::target::word
- ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word ClassTable_table_offset = 16;
static constexpr dart::compiler::target::word Closure_context_offset = 40;
static constexpr dart::compiler::target::word
Closure_delayed_type_arguments_offset = 24;
@@ -3441,11 +3445,15 @@
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
static constexpr dart::compiler::target::word
ICData_receivers_static_type_offset = 32;
-static constexpr dart::compiler::target::word Isolate_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
-static constexpr dart::compiler::target::word Isolate_object_store_offset = 64;
+static constexpr dart::compiler::target::word
+ Isolate_cached_object_store_offset = 64;
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word LinkedHashMap_data_offset = 32;
static constexpr dart::compiler::target::word
@@ -3868,9 +3876,6 @@
static constexpr dart::compiler::target::word
AOT_Class_host_type_arguments_field_offset_in_words_offset = 104;
static constexpr dart::compiler::target::word
- AOT_ClassTable_shared_class_table_offset = 16;
-static constexpr dart::compiler::target::word AOT_ClassTable_table_offset = 8;
-static constexpr dart::compiler::target::word
AOT_SharedClassTable_class_heap_stats_table_offset = 0;
static constexpr dart::compiler::target::word AOT_Closure_context_offset = 20;
static constexpr dart::compiler::target::word
@@ -3913,18 +3918,20 @@
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedMask = 3;
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedShift = 0;
static constexpr dart::compiler::target::word AOT_ICData_entries_offset = 12;
-static constexpr dart::compiler::target::word AOT_Isolate_class_table_offset =
- 36;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_shared_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_class_table_table_offset = 40;
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
20;
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
24;
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
28;
-static constexpr dart::compiler::target::word AOT_Isolate_object_store_offset =
- 32;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_object_store_offset = 32;
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
- 60;
+ 48;
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 16;
static constexpr dart::compiler::target::word AOT_LinkedHashMap_data_offset =
16;
@@ -4386,9 +4393,6 @@
static constexpr dart::compiler::target::word
AOT_Class_host_type_arguments_field_offset_in_words_offset = 184;
static constexpr dart::compiler::target::word
- AOT_ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word AOT_ClassTable_table_offset = 16;
-static constexpr dart::compiler::target::word
AOT_SharedClassTable_class_heap_stats_table_offset = 0;
static constexpr dart::compiler::target::word AOT_Closure_context_offset = 40;
static constexpr dart::compiler::target::word
@@ -4431,18 +4435,20 @@
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedMask = 3;
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedShift = 0;
static constexpr dart::compiler::target::word AOT_ICData_entries_offset = 24;
-static constexpr dart::compiler::target::word AOT_Isolate_class_table_offset =
- 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
40;
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
48;
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
56;
-static constexpr dart::compiler::target::word AOT_Isolate_object_store_offset =
- 64;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_object_store_offset = 64;
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
- 120;
+ 96;
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word AOT_LinkedHashMap_data_offset =
32;
@@ -4910,9 +4916,6 @@
static constexpr dart::compiler::target::word
AOT_Class_host_type_arguments_field_offset_in_words_offset = 184;
static constexpr dart::compiler::target::word
- AOT_ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word AOT_ClassTable_table_offset = 16;
-static constexpr dart::compiler::target::word
AOT_SharedClassTable_class_heap_stats_table_offset = 0;
static constexpr dart::compiler::target::word AOT_Closure_context_offset = 40;
static constexpr dart::compiler::target::word
@@ -4955,18 +4958,20 @@
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedMask = 3;
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedShift = 0;
static constexpr dart::compiler::target::word AOT_ICData_entries_offset = 24;
-static constexpr dart::compiler::target::word AOT_Isolate_class_table_offset =
- 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
40;
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
48;
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
56;
-static constexpr dart::compiler::target::word AOT_Isolate_object_store_offset =
- 64;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_object_store_offset = 64;
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
- 120;
+ 96;
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word AOT_LinkedHashMap_data_offset =
32;
@@ -5433,9 +5438,6 @@
static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 44;
static constexpr dart::compiler::target::word
AOT_Class_host_type_arguments_field_offset_in_words_offset = 104;
-static constexpr dart::compiler::target::word
- AOT_ClassTable_shared_class_table_offset = 16;
-static constexpr dart::compiler::target::word AOT_ClassTable_table_offset = 8;
static constexpr dart::compiler::target::word AOT_Closure_context_offset = 20;
static constexpr dart::compiler::target::word
AOT_Closure_delayed_type_arguments_offset = 12;
@@ -5477,16 +5479,18 @@
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedMask = 3;
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedShift = 0;
static constexpr dart::compiler::target::word AOT_ICData_entries_offset = 12;
-static constexpr dart::compiler::target::word AOT_Isolate_class_table_offset =
- 36;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_shared_class_table_offset = 36;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_class_table_table_offset = 40;
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
20;
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
24;
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
28;
-static constexpr dart::compiler::target::word AOT_Isolate_object_store_offset =
- 32;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_object_store_offset = 32;
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 16;
static constexpr dart::compiler::target::word AOT_LinkedHashMap_data_offset =
16;
@@ -5944,9 +5948,6 @@
static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 88;
static constexpr dart::compiler::target::word
AOT_Class_host_type_arguments_field_offset_in_words_offset = 184;
-static constexpr dart::compiler::target::word
- AOT_ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word AOT_ClassTable_table_offset = 16;
static constexpr dart::compiler::target::word AOT_Closure_context_offset = 40;
static constexpr dart::compiler::target::word
AOT_Closure_delayed_type_arguments_offset = 24;
@@ -5988,16 +5989,18 @@
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedMask = 3;
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedShift = 0;
static constexpr dart::compiler::target::word AOT_ICData_entries_offset = 24;
-static constexpr dart::compiler::target::word AOT_Isolate_class_table_offset =
- 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
40;
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
48;
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
56;
-static constexpr dart::compiler::target::word AOT_Isolate_object_store_offset =
- 64;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_object_store_offset = 64;
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word AOT_LinkedHashMap_data_offset =
32;
@@ -6461,9 +6464,6 @@
static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 88;
static constexpr dart::compiler::target::word
AOT_Class_host_type_arguments_field_offset_in_words_offset = 184;
-static constexpr dart::compiler::target::word
- AOT_ClassTable_shared_class_table_offset = 32;
-static constexpr dart::compiler::target::word AOT_ClassTable_table_offset = 16;
static constexpr dart::compiler::target::word AOT_Closure_context_offset = 40;
static constexpr dart::compiler::target::word
AOT_Closure_delayed_type_arguments_offset = 24;
@@ -6505,16 +6505,18 @@
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedMask = 3;
static constexpr dart::compiler::target::word AOT_ICData_NumArgsTestedShift = 0;
static constexpr dart::compiler::target::word AOT_ICData_entries_offset = 24;
-static constexpr dart::compiler::target::word AOT_Isolate_class_table_offset =
- 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_shared_class_table_offset = 72;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_class_table_table_offset = 80;
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
40;
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
48;
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
56;
-static constexpr dart::compiler::target::word AOT_Isolate_object_store_offset =
- 64;
+static constexpr dart::compiler::target::word
+ AOT_Isolate_cached_object_store_offset = 64;
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
static constexpr dart::compiler::target::word AOT_LinkedHashMap_data_offset =
32;
diff --git a/runtime/vm/compiler/runtime_offsets_list.h b/runtime/vm/compiler/runtime_offsets_list.h
index 229ff56..6294726 100644
--- a/runtime/vm/compiler/runtime_offsets_list.h
+++ b/runtime/vm/compiler/runtime_offsets_list.h
@@ -66,8 +66,6 @@
FIELD(Class, num_type_arguments_offset) \
FIELD(Class, super_type_offset) \
FIELD(Class, host_type_arguments_field_offset_in_words_offset) \
- FIELD(ClassTable, shared_class_table_offset) \
- FIELD(ClassTable, table_offset) \
NOT_IN_PRODUCT(FIELD(SharedClassTable, class_heap_stats_table_offset)) \
FIELD(Closure, context_offset) \
FIELD(Closure, delayed_type_arguments_offset) \
@@ -106,11 +104,12 @@
PRECOMP_NO_CHECK(FIELD(ICData, owner_offset)) \
PRECOMP_NO_CHECK(FIELD(ICData, state_bits_offset)) \
NOT_IN_PRECOMPILED_RUNTIME(FIELD(ICData, receivers_static_type_offset)) \
- FIELD(Isolate, class_table_offset) \
+ FIELD(Isolate, shared_class_table_offset) \
+ FIELD(Isolate, cached_class_table_table_offset) \
FIELD(Isolate, current_tag_offset) \
FIELD(Isolate, default_tag_offset) \
FIELD(Isolate, ic_miss_code_offset) \
- FIELD(Isolate, object_store_offset) \
+ FIELD(Isolate, cached_object_store_offset) \
NOT_IN_PRODUCT(FIELD(Isolate, single_step_offset)) \
FIELD(Isolate, user_tag_offset) \
FIELD(LinkedHashMap, data_offset) \
diff --git a/runtime/vm/compiler/stub_code_compiler.h b/runtime/vm/compiler/stub_code_compiler.h
index e4a9429..35f23fd 100644
--- a/runtime/vm/compiler/stub_code_compiler.h
+++ b/runtime/vm/compiler/stub_code_compiler.h
@@ -50,7 +50,6 @@
const Object& context_allocation_stub);
#endif
-#if !defined(DART_PRECOMPILED_RUNTIME)
static RawArray* BuildStaticCallsTable(
Zone* zone,
compiler::UnresolvedPcRelativeCalls* unresolved_calls);
@@ -121,31 +120,12 @@
static void GenerateJITCallbackTrampolines(Assembler* assembler,
intptr_t next_callback_id);
-
-#endif // !defined(DART_PRECOMPILED_RUNTIME)
};
} // namespace compiler
enum DeoptStubKind { kLazyDeoptFromReturn, kLazyDeoptFromThrow, kEagerDeopt };
-// Invocation mode for TypeCheck runtime entry that describes
-// where we are calling it from.
-enum TypeCheckMode {
- // TypeCheck is invoked from LazySpecializeTypeTest stub.
- // It should replace stub on the type with a specialized version.
- kTypeCheckFromLazySpecializeStub,
-
- // TypeCheck is invoked from the SlowTypeTest stub.
- // This means that cache can be lazily created (if needed)
- // and dst_name can be fetched from the pool.
- kTypeCheckFromSlowStub,
-
- // TypeCheck is invoked from normal inline AssertAssignable.
- // Both cache and dst_name must be already populated.
- kTypeCheckFromInline
-};
-
// Zap value used to indicate unused CODE_REG in deopt.
static const uword kZapCodeReg = 0xf1f1f1f1;
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 1e817e7..8a6858b 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -16,6 +16,7 @@
#include "vm/class_id.h"
#include "vm/code_entry_kind.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/locations.h"
#include "vm/constants.h"
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 0730c37..ced8835 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -15,6 +15,7 @@
#include "vm/class_id.h"
#include "vm/code_entry_kind.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/locations.h"
#include "vm/constants.h"
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index 53b0cc1..15a5c89 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -15,6 +15,7 @@
#include "vm/class_id.h"
#include "vm/code_entry_kind.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/locations.h"
#include "vm/constants.h"
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index d981a03..9a2b2c7 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -17,6 +17,7 @@
#include "vm/class_id.h"
#include "vm/code_entry_kind.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/constants.h"
#include "vm/instructions.h"
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 48e015e..b068e7f 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -947,6 +947,8 @@
float ReciprocalSqrtEstimate(float op);
float ReciprocalSqrtStep(float op1, float op2);
+constexpr uword kBreakInstructionFiller = 0xE1200070; // bkpt #0
+
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_ARM_H_
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index a7852d6..9929f0d 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -1326,6 +1326,8 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
};
+const uword kBreakInstructionFiller = 0xD4200000D4200000L; // brk #0; brk #0
+
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_ARM64_H_
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index 398fdcc..2f7d689 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -247,6 +247,8 @@
static constexpr ExtensionStrategy kArgumentStackExtension = kExtendedTo4;
};
+const uword kBreakInstructionFiller = 0xCCCCCCCC;
+
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_IA32_H_
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index fe2e01d..f4030c9 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -408,6 +408,8 @@
// becomes important to us.
const int MAX_NOP_SIZE = 8;
+const uword kBreakInstructionFiller = 0xCCCCCCCCCCCCCCCCL;
+
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_X64_H_
diff --git a/runtime/vm/cpu_x64.cc b/runtime/vm/cpu_x64.cc
index c892d85..7f5981c 100644
--- a/runtime/vm/cpu_x64.cc
+++ b/runtime/vm/cpu_x64.cc
@@ -8,7 +8,6 @@
#include "vm/cpu.h"
#include "vm/cpu_x64.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/constants.h"
#include "vm/cpuinfo.h"
#include "vm/heap/heap.h"
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index ad905b6..f295519 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -248,7 +248,9 @@
std::unique_ptr<IsolateGroupSource> source(
new IsolateGroupSource(nullptr, kVmIsolateName, vm_isolate_snapshot,
instructions_snapshot, nullptr, -1, api_flags));
- auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr);
+ // ObjectStore should be created later, after null objects are initialized.
+ auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr,
+ /*object_store=*/nullptr);
group->CreateHeap(/*is_vm_isolate=*/true,
/*is_service_or_kernel_isolate=*/false);
IsolateGroup::RegisterIsolateGroup(group);
@@ -265,7 +267,8 @@
StackZone zone(T);
HandleScope handle_scope(T);
Object::InitNullAndBool(vm_isolate_);
- ObjectStore::Init(vm_isolate_);
+ vm_isolate_->set_object_store(new ObjectStore());
+ vm_isolate_->isolate_object_store()->Init();
TargetCPUFeatures::Init();
Object::Init(vm_isolate_);
ArgumentsDescriptor::Init();
@@ -653,25 +656,35 @@
return Snapshot::IsFull(isolate_kind);
}
-RawError* Dart::InitializeIsolate(const uint8_t* snapshot_data,
- const uint8_t* snapshot_instructions,
- const uint8_t* kernel_buffer,
- intptr_t kernel_buffer_size,
- void* isolate_data) {
- // Initialize the new isolate.
- Thread* T = Thread::Current();
- Isolate* I = T->isolate();
-#if defined(SUPPORT_TIMELINE)
- TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
- "InitializeIsolate");
- tbes.SetNumArguments(1);
- tbes.CopyArgument(0, "isolateName", I->name());
-#endif
- ASSERT(I != NULL);
- StackZone zone(T);
- HandleScope handle_scope(T);
- ObjectStore::Init(I);
+#if defined(DART_PRECOMPILED_RUNTIME)
+static bool CloneIntoChildIsolateAOT(Thread* T,
+ Isolate* I,
+ IsolateGroup* source_isolate_group) {
+ // In AOT we speed up isolate spawning by copying donor's isolate structure.
+ Isolate* donor_isolate = source_isolate_group != nullptr
+ ? source_isolate_group->FirstIsolate()
+ : nullptr;
+ if (donor_isolate == nullptr) {
+ return false;
+ }
+ I->isolate_object_store()->Init();
+ I->isolate_object_store()->PreallocateObjects();
+ // Initialize field_table with initial values.
+ I->set_field_table(T, donor_isolate->saved_initial_field_table()->Clone());
+ I->set_saved_initial_field_table(
+ donor_isolate->saved_initial_field_table_shareable());
+ ReversePcLookupCache::BuildAndAttachToIsolate(I);
+ return true;
+}
+#endif
+
+RawError* Dart::InitIsolateFromSnapshot(Thread* T,
+ Isolate* I,
+ const uint8_t* snapshot_data,
+ const uint8_t* snapshot_instructions,
+ const uint8_t* kernel_buffer,
+ intptr_t kernel_buffer_size) {
Error& error = Error::Handle(T->zone());
error = Object::Init(I, kernel_buffer, kernel_buffer_size);
if (!error.IsNull()) {
@@ -681,8 +694,8 @@
// Read the snapshot and setup the initial state.
#if defined(SUPPORT_TIMELINE)
TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
- "ReadIsolateSnapshot");
-#endif
+ "ReadProgramSnapshot");
+#endif // defined(SUPPORT_TIMELINE)
// TODO(turnidge): Remove once length is not part of the snapshot.
const Snapshot* snapshot = Snapshot::SetupFromBuffer(snapshot_data);
if (snapshot == NULL) {
@@ -700,7 +713,7 @@
OS::PrintErr("Size of isolate snapshot = %" Pd "\n", snapshot->length());
}
FullSnapshotReader reader(snapshot, snapshot_instructions, T);
- const Error& error = Error::Handle(reader.ReadIsolateSnapshot());
+ const Error& error = Error::Handle(reader.ReadProgramSnapshot());
if (!error.IsNull()) {
return error.raw();
}
@@ -714,7 +727,7 @@
tbes.FormatArgument(1, "heapSize", "%" Pd64,
I->heap()->UsedInWords(Heap::kOld) * kWordSize);
}
-#endif // !defined(PRODUCT)
+#endif // defined(SUPPORT_TIMELINE)
if (FLAG_trace_isolates) {
I->heap()->PrintSizes();
MegamorphicCacheTable::PrintSizes(I);
@@ -727,6 +740,89 @@
}
}
+ return Error::null();
+}
+
+#if defined(DART_PRECOMPILED_RUNTIME)
+static void PrintLLVMConstantPool(Thread* T, Isolate* I) {
+ StackZone printing_zone(T);
+ HandleScope printing_scope(T);
+ TextBuffer b(1000);
+ const auto& constants =
+ GrowableObjectArray::Handle(I->object_store()->llvm_constant_pool());
+ if (constants.IsNull()) {
+ b.AddString("No constant pool information in snapshot.\n\n");
+ } else {
+ auto const len = constants.Length();
+ b.Printf("Constant pool contents (length %" Pd "):\n", len);
+ auto& obj = Object::Handle();
+ for (intptr_t i = 0; i < len; i++) {
+ obj = constants.At(i);
+ b.Printf(" %5" Pd ": ", i);
+ if (obj.IsString()) {
+ b.AddChar('"');
+ b.AddEscapedString(obj.ToCString());
+ b.AddChar('"');
+ } else {
+ b.AddString(obj.ToCString());
+ }
+ b.AddChar('\n');
+ }
+ b.AddString("End of constant pool.\n\n");
+ }
+ const auto& functions =
+ GrowableObjectArray::Handle(I->object_store()->llvm_function_pool());
+ if (functions.IsNull()) {
+ b.AddString("No function pool information in snapshot.\n\n");
+ } else {
+ auto const len = functions.Length();
+ b.Printf("Function pool contents (length %" Pd "):\n", len);
+ auto& obj = Function::Handle();
+ for (intptr_t i = 0; i < len; i++) {
+ obj ^= functions.At(i);
+ ASSERT(!obj.IsNull());
+ b.Printf(" %5" Pd ": %s\n", i, obj.ToFullyQualifiedCString());
+ }
+ b.AddString("End of function pool.\n\n");
+ }
+ THR_Print("%s", b.buf());
+}
+#endif
+
+RawError* Dart::InitializeIsolate(const uint8_t* snapshot_data,
+ const uint8_t* snapshot_instructions,
+ const uint8_t* kernel_buffer,
+ intptr_t kernel_buffer_size,
+ IsolateGroup* source_isolate_group,
+ void* isolate_data) {
+ // Initialize the new isolate.
+ Thread* T = Thread::Current();
+ Isolate* I = T->isolate();
+#if defined(SUPPORT_TIMELINE)
+ TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
+ "InitializeIsolate");
+ tbes.SetNumArguments(1);
+ tbes.CopyArgument(0, "isolateName", I->name());
+#endif
+ ASSERT(I != NULL);
+ StackZone zone(T);
+ HandleScope handle_scope(T);
+ bool was_child_cloned_into_existing_isolate = false;
+#if defined(DART_PRECOMPILED_RUNTIME)
+ if (CloneIntoChildIsolateAOT(T, I, source_isolate_group)) {
+ was_child_cloned_into_existing_isolate = true;
+ } else {
+#endif
+ const Error& error = Error::Handle(
+ InitIsolateFromSnapshot(T, I, snapshot_data, snapshot_instructions,
+ kernel_buffer, kernel_buffer_size));
+ if (!error.IsNull()) {
+ return error.raw();
+ }
+#if defined(DART_PRECOMPILED_RUNTIME)
+ }
+#endif
+
Object::VerifyBuiltinVtables();
DEBUG_ONLY(I->heap()->Verify(kForbidMarked));
@@ -735,47 +831,7 @@
ASSERT(I->object_store()->megamorphic_call_miss_code() != Code::null());
ASSERT(I->object_store()->build_method_extractor_code() != Code::null());
if (FLAG_print_llvm_constant_pool) {
- StackZone printing_zone(T);
- HandleScope printing_scope(T);
- TextBuffer b(1000);
- const auto& constants =
- GrowableObjectArray::Handle(I->object_store()->llvm_constant_pool());
- if (constants.IsNull()) {
- b.AddString("No constant pool information in snapshot.\n\n");
- } else {
- auto const len = constants.Length();
- b.Printf("Constant pool contents (length %" Pd "):\n", len);
- auto& obj = Object::Handle();
- for (intptr_t i = 0; i < len; i++) {
- obj = constants.At(i);
- b.Printf(" %5" Pd ": ", i);
- if (obj.IsString()) {
- b.AddChar('"');
- b.AddEscapedString(obj.ToCString());
- b.AddChar('"');
- } else {
- b.AddString(obj.ToCString());
- }
- b.AddChar('\n');
- }
- b.AddString("End of constant pool.\n\n");
- }
- const auto& functions =
- GrowableObjectArray::Handle(I->object_store()->llvm_function_pool());
- if (functions.IsNull()) {
- b.AddString("No function pool information in snapshot.\n\n");
- } else {
- auto const len = functions.Length();
- b.Printf("Function pool contents (length %" Pd "):\n", len);
- auto& obj = Function::Handle();
- for (intptr_t i = 0; i < len; i++) {
- obj ^= functions.At(i);
- ASSERT(!obj.IsNull());
- b.Printf(" %5" Pd ": %s\n", i, obj.ToFullyQualifiedCString());
- }
- b.AddString("End of function pool.\n\n");
- }
- THR_Print("%s", b.buf());
+ PrintLLVMConstantPool(T, I);
}
#else
// JIT: The megamorphic call miss function and code come from the snapshot in
@@ -794,13 +850,20 @@
I->set_ic_miss_code(StubCode::SwitchableCallMiss());
if ((snapshot_data == NULL) || (kernel_buffer != NULL)) {
- const Error& error = Error::Handle(I->object_store()->PreallocateObjects());
+ Error& error = Error::Handle();
+ error ^= I->object_store()->PreallocateObjects();
+ if (!error.IsNull()) {
+ return error.raw();
+ }
+ error ^= I->isolate_object_store()->PreallocateObjects();
if (!error.IsNull()) {
return error.raw();
}
}
- I->heap()->InitGrowthControl();
+ if (!was_child_cloned_into_existing_isolate) {
+ I->heap()->InitGrowthControl();
+ }
I->set_init_callback_data(isolate_data);
if (FLAG_print_class_table) {
I->class_table()->Print();
diff --git a/runtime/vm/dart.h b/runtime/vm/dart.h
index 0c7b6e8..537c0a7 100644
--- a/runtime/vm/dart.h
+++ b/runtime/vm/dart.h
@@ -59,7 +59,14 @@
const uint8_t* snapshot_instructions,
const uint8_t* kernel_buffer,
intptr_t kernel_buffer_size,
+ IsolateGroup* source_isolate_group,
void* data);
+ static RawError* InitIsolateFromSnapshot(Thread* T,
+ Isolate* I,
+ const uint8_t* snapshot_data,
+ const uint8_t* snapshot_instructions,
+ const uint8_t* kernel_buffer,
+ intptr_t kernel_buffer_size);
static void RunShutdownCallback();
static void ShutdownIsolate(Isolate* isolate);
static void ShutdownIsolate();
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 3ff092c..6d3cf56 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -22,11 +22,7 @@
#include "vm/debugger.h"
#include "vm/dwarf.h"
#include "vm/elf.h"
-#if !defined(DART_PRECOMPILED_RUNTIME)
-#include "vm/kernel_loader.h"
-#endif
#include "platform/unicode.h"
-#include "vm/compiler/aot/precompiler.h"
#include "vm/exceptions.h"
#include "vm/flags.h"
#include "vm/growable_array.h"
@@ -59,6 +55,11 @@
#include "vm/uri.h"
#include "vm/version.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/aot/precompiler.h"
+#include "vm/kernel_loader.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
// Facilitate quick access to the current zone once we have the current thread.
@@ -1141,10 +1142,10 @@
// Api Handles when an error is encountered.
T->EnterApiScope();
const Error& error_obj = Error::Handle(
- Z, Dart::InitializeIsolate(source->snapshot_data,
- source->snapshot_instructions,
- source->kernel_buffer,
- source->kernel_buffer_size, isolate_data));
+ Z, Dart::InitializeIsolate(
+ source->snapshot_data, source->snapshot_instructions,
+ source->kernel_buffer, source->kernel_buffer_size,
+ is_new_group ? nullptr : group, isolate_data));
if (error_obj.IsNull()) {
#if defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME)
if (FLAG_check_function_fingerprints && source->kernel_buffer == NULL) {
@@ -1193,9 +1194,33 @@
return false;
}
+Isolate* CreateWithinExistingIsolateGroupAOT(IsolateGroup* group,
+ const char* name,
+ char** error) {
+#if defined(DART_PRECOMPILED_RUNTIME)
+ API_TIMELINE_DURATION(Thread::Current());
+ CHECK_NO_ISOLATE(Isolate::Current());
+
+ auto spawning_group = group;
+
+ Isolate* isolate = reinterpret_cast<Isolate*>(
+ CreateIsolate(spawning_group, /*is_new_group=*/false, name,
+ /*isolate_data=*/nullptr, error));
+ if (isolate == nullptr) return nullptr;
+
+ auto source = spawning_group->source();
+ ASSERT(isolate->source() == source);
+
+ return isolate;
+#else
+ UNREACHABLE();
+#endif
+}
+
Isolate* CreateWithinExistingIsolateGroup(IsolateGroup* group,
const char* name,
char** error) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
API_TIMELINE_DURATION(Thread::Current());
CHECK_NO_ISOLATE(Isolate::Current());
@@ -1218,9 +1243,6 @@
ASSERT(isolate->source() == source);
if (source->script_kernel_buffer != nullptr) {
-#if defined(DART_PRECOMPILED_RUNTIME)
- UNREACHABLE();
-#else
Dart_EnterScope();
{
Thread* T = Thread::Current();
@@ -1250,7 +1272,6 @@
isolate->object_store()->set_root_library(Library::Cast(tmp));
}
Dart_ExitScope();
-#endif // defined(DART_PRECOMPILED_RUNTIME)
}
// If we are running in AppJIT training mode we'll have to remap class ids.
@@ -1321,7 +1342,9 @@
isolate->isolate_group_ = group;
group->RegisterIsolateLocked(isolate);
- isolate->class_table()->shared_class_table_ = group->class_table();
+ isolate->class_table()->shared_class_table_ =
+ group->shared_class_table();
+ isolate->set_shared_class_table(group->shared_class_table());
// Even though the mutator thread was descheduled, it will still
// retain its [Thread] structure with valid isolate/isolate_group
@@ -1353,6 +1376,9 @@
ASSERT(Thread::Current()->isolate_group() == isolate->group());
return isolate;
+#else
+ UNREACHABLE();
+#endif
}
DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags) {
diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h
index 65ed764..16c875d 100644
--- a/runtime/vm/dart_api_impl.h
+++ b/runtime/vm/dart_api_impl.h
@@ -353,6 +353,9 @@
Isolate* CreateWithinExistingIsolateGroup(IsolateGroup* group,
const char* name,
char** error);
+Isolate* CreateWithinExistingIsolateGroupAOT(IsolateGroup* group,
+ const char* name,
+ char** error);
} // namespace dart.
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 42d53be..88a75f6 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -6,8 +6,6 @@
#include "platform/safe_stack.h"
#include "vm/class_finalizer.h"
-#include "vm/compiler/frontend/bytecode_reader.h"
-#include "vm/compiler/jit/compiler.h"
#include "vm/debugger.h"
#include "vm/dispatch_table.h"
#include "vm/heap/safepoint.h"
@@ -19,6 +17,11 @@
#include "vm/stub_code.h"
#include "vm/symbols.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/frontend/bytecode_reader.h"
+#include "vm/compiler/jit/compiler.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
DECLARE_FLAG(bool, enable_interpreter);
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index d121a55..adb3591 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -8,13 +8,13 @@
#include "platform/address_sanitizer.h"
+#include "vm/code_descriptors.h"
#include "vm/code_patcher.h"
+#include "vm/compiler/api/deopt_id.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
-#include "vm/compiler/frontend/bytecode_reader.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/dart_entry.h"
-#include "vm/deopt_instructions.h"
#include "vm/flags.h"
#include "vm/globals.h"
#include "vm/interpreter.h"
@@ -41,6 +41,11 @@
#include "vm/token_position.h"
#include "vm/visitor.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/frontend/bytecode_reader.h"
+#include "vm/deopt_instructions.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
DEFINE_FLAG(bool,
diff --git a/runtime/vm/debugger_x64.cc b/runtime/vm/debugger_x64.cc
index 883880f..3cd1810 100644
--- a/runtime/vm/debugger_x64.cc
+++ b/runtime/vm/debugger_x64.cc
@@ -8,7 +8,6 @@
#include "vm/debugger.h"
#include "vm/code_patcher.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/cpu.h"
#include "vm/instructions.h"
#include "vm/stub_code.h"
diff --git a/runtime/vm/deopt_instructions.h b/runtime/vm/deopt_instructions.h
index 926ea1b..57fff85 100644
--- a/runtime/vm/deopt_instructions.h
+++ b/runtime/vm/deopt_instructions.h
@@ -4,10 +4,10 @@
#ifndef RUNTIME_VM_DEOPT_INSTRUCTIONS_H_
#define RUNTIME_VM_DEOPT_INSTRUCTIONS_H_
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/allocation.h"
#include "vm/code_descriptors.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/compiler/backend/locations.h"
#include "vm/deferred_objects.h"
@@ -19,6 +19,7 @@
namespace dart {
+class Location;
class Value;
class MaterializeObjectInstr;
class StackFrame;
@@ -615,4 +616,5 @@
} // namespace dart
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // RUNTIME_VM_DEOPT_INSTRUCTIONS_H_
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index 762326e..76d0097 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -69,8 +69,9 @@
: stacktrace_(StackTrace::Cast(stacktrace)),
cur_index_(0),
dropped_frames_(0) {
- ASSERT(stacktrace_.raw() ==
- Isolate::Current()->object_store()->preallocated_stack_trace());
+ ASSERT(
+ stacktrace_.raw() ==
+ Isolate::Current()->isolate_object_store()->preallocated_stack_trace());
}
~PreallocatedStackTraceBuilder() {}
@@ -815,11 +816,12 @@
ASSERT(incoming_exception.raw() ==
isolate->object_store()->out_of_memory());
const UnhandledException& error = UnhandledException::Handle(
- zone, isolate->object_store()->preallocated_unhandled_exception());
+ zone,
+ isolate->isolate_object_store()->preallocated_unhandled_exception());
thread->long_jump_base()->Jump(1, error);
UNREACHABLE();
}
- stacktrace = isolate->object_store()->preallocated_stack_trace();
+ stacktrace = isolate->isolate_object_store()->preallocated_stack_trace();
PreallocatedStackTraceBuilder frame_builder(stacktrace);
ASSERT(existing_stacktrace.IsNull() ||
(existing_stacktrace.raw() == stacktrace.raw()));
diff --git a/runtime/vm/ffi_callback_trampolines.cc b/runtime/vm/ffi_callback_trampolines.cc
index 6475a3d..4bf926e 100644
--- a/runtime/vm/ffi_callback_trampolines.cc
+++ b/runtime/vm/ffi_callback_trampolines.cc
@@ -3,11 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
#include "vm/ffi_callback_trampolines.h"
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/code_comments.h"
#include "vm/code_observers.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/exceptions.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
diff --git a/runtime/vm/ffi_callback_trampolines.h b/runtime/vm/ffi_callback_trampolines.h
index 7328828..26143be 100644
--- a/runtime/vm/ffi_callback_trampolines.h
+++ b/runtime/vm/ffi_callback_trampolines.h
@@ -1,15 +1,17 @@
// Copyright (c) 2019, 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.
+#ifndef RUNTIME_VM_FFI_CALLBACK_TRAMPOLINES_H_
+#define RUNTIME_VM_FFI_CALLBACK_TRAMPOLINES_H_
#include "platform/allocation.h"
#include "platform/growable_array.h"
-#include "vm/compiler/stub_code_compiler.h"
#include "vm/flag_list.h"
#include "vm/virtual_memory.h"
-#ifndef RUNTIME_VM_FFI_CALLBACK_TRAMPOLINES_H_
-#define RUNTIME_VM_FFI_CALLBACK_TRAMPOLINES_H_
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/stub_code_compiler.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
diff --git a/runtime/vm/field_table.cc b/runtime/vm/field_table.cc
index 5a248c1..96d3f5a 100644
--- a/runtime/vm/field_table.cc
+++ b/runtime/vm/field_table.cc
@@ -95,7 +95,24 @@
Thread::Current()->field_table_values_ = table_;
}
+FieldTable* FieldTable::Clone() {
+ FieldTable* clone = new FieldTable();
+ auto new_table = static_cast<RawInstance**>(
+ malloc(capacity_ * sizeof(RawInstance*))); // NOLINT
+ memmove(new_table, table_, top_ * sizeof(RawInstance*));
+ ASSERT(clone->table_ == nullptr);
+ clone->table_ = new_table;
+ clone->capacity_ = capacity_;
+ clone->top_ = top_;
+ return clone;
+}
+
void FieldTable::VisitObjectPointers(ObjectPointerVisitor* visitor) {
+ // GC might try to visit field table before it's isolate done setting it up.
+ if (table_ == nullptr) {
+ return;
+ }
+
ASSERT(visitor != NULL);
visitor->set_gc_root_type("static fields table");
visitor->VisitPointers(reinterpret_cast<RawObject**>(&table_[0]),
diff --git a/runtime/vm/field_table.h b/runtime/vm/field_table.h
index e89c733..08e83d8 100644
--- a/runtime/vm/field_table.h
+++ b/runtime/vm/field_table.h
@@ -55,6 +55,8 @@
}
void SetAt(intptr_t index, RawInstance* raw_instance);
+ FieldTable* Clone();
+
void VisitObjectPointers(ObjectPointerVisitor* visitor);
static const int kInitialCapacity = 512;
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index f01d690..f1344a9 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -59,9 +59,8 @@
// The syntax used is the same as that for FLAG_LIST below, as these flags are
// automatically included in FLAG_LIST.
#define VM_GLOBAL_FLAG_LIST(P, R, C, D) \
- P(dwarf_stack_traces, bool, false, \
- "Emit DWARF line number and inlining info" \
- "in dylib snapshots and don't symbolize stack traces.") \
+ P(dwarf_stack_traces_mode, bool, false, \
+ "Use --[no-]dwarf-stack-traces instead.") \
P(causal_async_stacks, bool, !USING_PRODUCT, "Improved async stacks") \
P(lazy_async_stacks, bool, false, "Reconstruct async stacks from listeners") \
P(use_bare_instructions, bool, true, "Enable bare instructions mode.") \
@@ -195,9 +194,9 @@
P(null_safety, bool, false, \
"Respect the nullability of types in casts and instance checks.") \
P(use_table_dispatch, bool, true, "Enable dispatch table based calls.") \
- P(retain_dispatched_functions, bool, !USING_PRODUCT, \
- "Serialize function objects for code in the dispatch table even if " \
- "not needed in the precompiled runtime") \
+ P(retain_function_objects, bool, true, \
+ "Serialize function objects for all code objects even if not otherwise " \
+ "needed in the precompiled runtime.") \
P(enable_isolate_groups, bool, false, "Enable isolate group support.") \
P(show_invisible_frames, bool, false, \
"Show invisible frames in stack traces.") \
diff --git a/runtime/vm/handles.h b/runtime/vm/handles.h
index 2b3de59..28ec9cc 100644
--- a/runtime/vm/handles.h
+++ b/runtime/vm/handles.h
@@ -207,6 +207,7 @@
friend class HandleScope;
friend class Dart;
+ friend class IsolateObjectStore;
friend class ObjectStore;
friend class ThreadState;
DISALLOW_ALLOCATION();
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 59abade..0403d54 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -87,15 +87,16 @@
if (LIKELY(addr != 0)) {
return addr;
}
+ if (new_space_.GrowthControlState()) {
+ // This call to CollectGarbage might end up "reusing" a collection spawned
+ // from a different thread and will be racing to allocate the requested
+ // memory with other threads being released after the collection.
+ CollectGarbage(kNew);
- // This call to CollectGarbage might end up "reusing" a collection spawned
- // from a different thread and will be racing to allocate the requested
- // memory with other threads being released after the collection.
- CollectGarbage(kNew);
-
- addr = new_space_.TryAllocate(thread, size);
- if (LIKELY(addr != 0)) {
- return addr;
+ addr = new_space_.TryAllocate(thread, size);
+ if (LIKELY(addr != 0)) {
+ return addr;
+ }
}
// It is possible a GC doesn't clear enough space.
@@ -651,14 +652,17 @@
}
void Heap::InitGrowthControl() {
+ new_space_.InitGrowthControl();
old_space_.InitGrowthControl();
}
void Heap::SetGrowthControlState(bool state) {
+ new_space_.SetGrowthControlState(state);
old_space_.SetGrowthControlState(state);
}
bool Heap::GrowthControlState() {
+ ASSERT(new_space_.GrowthControlState() == old_space_.GrowthControlState());
return old_space_.GrowthControlState();
}
@@ -720,6 +724,11 @@
void Heap::CollectForDebugging() {
if (gc_on_nth_allocation_ == kNoForcedGarbageCollection) return;
+ if (Thread::Current()->IsAtSafepoint()) {
+ // CollectAllGarbage is not supported when we are at a safepoint.
+ // Allocating when at a safepoint is not a common case.
+ return;
+ }
gc_on_nth_allocation_--;
if (gc_on_nth_allocation_ == 0) {
CollectAllGarbage(kDebugging);
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index 69dc98b..e30b529 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -292,7 +292,7 @@
public:
explicit MarkingWeakVisitor(Thread* thread)
: HandleVisitor(thread),
- class_table_(thread->isolate_group()->class_table()) {}
+ class_table_(thread->isolate_group()->shared_class_table()) {}
void VisitHandle(uword addr) {
FinalizablePersistentHandle* handle =
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index a171b2f..16776bd 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1113,7 +1113,7 @@
const int64_t start = OS::GetCurrentMonotonicMicros();
// Perform various cleanup that relies on no tasks interfering.
- isolate_group->class_table()->FreeOldTables();
+ isolate_group->shared_class_table()->FreeOldTables();
isolate_group->ForEachIsolate(
[&](Isolate* isolate) { isolate->field_table()->FreeOldTables(); },
/*at_safepoint=*/true);
diff --git a/runtime/vm/heap/safepoint.h b/runtime/vm/heap/safepoint.h
index 8615b53..7c3dde1 100644
--- a/runtime/vm/heap/safepoint.h
+++ b/runtime/vm/heap/safepoint.h
@@ -51,6 +51,8 @@
void BlockForSafepoint(Thread* T);
+ bool IsOwnedByTheThread(Thread* thread) { return owner_ == thread; }
+
private:
void SafepointThreads(Thread* T);
void ResumeThreads(Thread* T);
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index ee11b71..1ca8bd9 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -449,7 +449,7 @@
ScavengerWeakVisitor(Thread* thread, Scavenger* scavenger)
: HandleVisitor(thread),
scavenger_(scavenger),
- class_table_(thread->isolate_group()->class_table()) {
+ class_table_(thread->isolate_group()->shared_class_table()) {
ASSERT(scavenger->heap_->isolate_group() == thread->isolate_group());
}
diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h
index d02d623..138a1a6 100644
--- a/runtime/vm/heap/scavenger.h
+++ b/runtime/vm/heap/scavenger.h
@@ -202,6 +202,16 @@
void MakeNewSpaceIterable() const;
int64_t FreeSpaceInWords(Isolate* isolate) const;
+ void InitGrowthControl() {
+ growth_control_ = true;
+ }
+
+ void SetGrowthControlState(bool state) {
+ growth_control_ = state;
+ }
+
+ bool GrowthControlState() { return growth_control_; }
+
bool scavenging() const { return scavenging_; }
private:
@@ -313,6 +323,8 @@
bool failed_to_promote_;
+ bool growth_control_;
+
// Protects new space during the allocation of new TLABs
mutable Mutex space_lock_;
diff --git a/runtime/vm/heap/sweeper.cc b/runtime/vm/heap/sweeper.cc
index 2a19fba..96cf844 100644
--- a/runtime/vm/heap/sweeper.cc
+++ b/runtime/vm/heap/sweeper.cc
@@ -4,7 +4,6 @@
#include "vm/heap/sweeper.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/globals.h"
#include "vm/heap/freelist.h"
#include "vm/heap/heap.h"
@@ -52,8 +51,7 @@
uword cursor = current;
uword end = current + obj_size;
while (cursor < end) {
- *reinterpret_cast<uword*>(cursor) =
- compiler::Assembler::GetBreakInstructionFiller();
+ *reinterpret_cast<uword*>(cursor) = kBreakInstructionFiller;
cursor += kWordSize;
}
} else {
diff --git a/runtime/vm/heap/weak_table.cc b/runtime/vm/heap/weak_table.cc
index b4cf28d..1bc9daf 100644
--- a/runtime/vm/heap/weak_table.cc
+++ b/runtime/vm/heap/weak_table.cc
@@ -135,7 +135,7 @@
void WeakTable::MergeOtherWeakTable(WeakTable* other) {
for (intptr_t i = 0; i < other->size(); i++) {
if (other->IsValidEntryAtExclusive(i)) {
- SetValue(other->ObjectAtExclusive(i), ValueIndex(i));
+ SetValueExclusive(other->ObjectAtExclusive(i), ValueIndex(i));
}
}
}
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index e344120..01d5d36 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -6,7 +6,6 @@
#include "platform/assert.h"
#include "vm/class_id.h"
-#include "vm/compiler/backend/code_statistics.h"
#include "vm/compiler/runtime_api.h"
#include "vm/dwarf.h"
#include "vm/elf.h"
@@ -22,6 +21,10 @@
#include "vm/timeline.h"
#include "vm/type_testing_stubs.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/backend/code_statistics.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
#if defined(DART_PRECOMPILER)
@@ -849,8 +852,7 @@
compiler::target::ObjectAlignment::kObjectAlignment) -
unaligned_size;
while (alignment_size > 0) {
- WriteWordLiteralText(
- compiler::Assembler::GetBreakInstructionFiller());
+ WriteWordLiteralText(kBreakInstructionFiller);
alignment_size -= sizeof(compiler::target::uword);
text_offset += sizeof(compiler::target::uword);
}
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index cb8c0e3..bc009ec 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -10,6 +10,7 @@
#include "vm/interpreter.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/compiler/backend/flow_graph_compiler.h"
@@ -272,7 +273,7 @@
const uword start = thread->top();
#ifndef PRODUCT
- auto table = thread->isolate_group()->class_table();
+ auto table = thread->isolate_group()->shared_class_table();
if (UNLIKELY(table->TraceAllocationFor(class_id))) {
return false;
}
diff --git a/runtime/vm/intrusive_dlist.h b/runtime/vm/intrusive_dlist.h
index 45c4365..421726d64 100644
--- a/runtime/vm/intrusive_dlist.h
+++ b/runtime/vm/intrusive_dlist.h
@@ -108,20 +108,20 @@
prev_ = nullptr;
}
- bool IsEmpty() {
+ bool IsEmpty() const {
bool result = next_ == this;
ASSERT(result == (prev_ == this));
return result;
}
- bool IsLinked() {
+ bool IsLinked() const {
ASSERT((next_ == nullptr) == (prev_ == nullptr));
return next_ != nullptr;
}
- IntrusiveDListEntry<T, N>* Prev() { return prev_; }
+ IntrusiveDListEntry<T, N>* Prev() const { return prev_; }
- IntrusiveDListEntry<T, N>* Next() { return next_; }
+ IntrusiveDListEntry<T, N>* Next() const { return next_; }
friend class IntrusiveDList<T, N>;
@@ -143,9 +143,9 @@
IntrusiveDListEntry<ContainerType, I>* entry)
: head_(head), entry_(entry) {}
- inline ContainerType* operator->() { return entry_->container(); }
+ inline ContainerType* operator->() const { return entry_->container(); }
- inline ContainerType* operator*() { return entry_->container(); }
+ inline ContainerType* operator*() const { return entry_->container(); }
inline bool operator==(const Iterator<ContainerType, I>& other) const {
return entry_ == other.entry_;
@@ -180,18 +180,18 @@
// NOTE: This function only checks whether [a] is linked inside *a*
// [IntrusiveDList].
- inline bool IsInList(T* a) { return convert(a)->IsLinked(); }
+ inline bool IsInList(T* a) const { return convert(a)->IsLinked(); }
inline void Remove(T* a) { convert(a)->Remove(); }
- inline bool IsEmpty() { return head_.IsEmpty(); }
+ inline bool IsEmpty() const { return head_.IsEmpty(); }
- inline T* First() {
+ inline T* First() const {
ASSERT(!IsEmpty());
return head_.Next()->container();
}
- inline T* Last() {
+ inline T* Last() const {
ASSERT(!IsEmpty());
return head_.Prev()->container();
}
@@ -230,7 +230,7 @@
private:
Entry head_;
- Entry* convert(T* entry) { return static_cast<Entry*>(entry); }
+ Entry* convert(T* entry) const { return static_cast<Entry*>(entry); }
};
} // namespace dart.
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index bd0db66..46fe80d 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -221,7 +221,8 @@
};
IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
- void* embedder_data)
+ void* embedder_data,
+ ObjectStore* object_store)
: embedder_data_(embedder_data),
isolates_lock_(new SafepointRwLock()),
isolates_(),
@@ -234,14 +235,37 @@
thread_registry_(new ThreadRegistry()),
safepoint_handler_(new SafepointHandler(this)),
shared_class_table_(new SharedClassTable()),
+ object_store_(object_store),
+#if defined(DART_PRECOMPILED_RUNTIME)
+ class_table_(new ClassTable(shared_class_table_.get())),
+#else
+ class_table_(nullptr),
+#endif
+ symbols_lock_(new SafepointRwLock()),
store_buffer_(new StoreBuffer()),
- heap_(nullptr) {
+ heap_(nullptr),
+ saved_unlinked_calls_(Array::null()) {
{
WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
id_ = isolate_group_random_->NextUInt64();
}
}
+IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
+ void* embedder_data)
+ : IsolateGroup(source,
+ embedder_data,
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ // in JIT, with --enable_isolate_groups keep object store
+ // on isolate, rather than on isolate group
+ FLAG_enable_isolate_groups ? nullptr :
+#endif
+ new ObjectStore()) {
+ if (object_store() != nullptr) {
+ object_store()->InitStubs();
+ }
+}
+
IsolateGroup::~IsolateGroup() {
// Finalize any weak persistent handles with a non-null referent.
FinalizeWeakPersistentHandlesVisitor visitor(this);
@@ -337,6 +361,10 @@
heap_ = std::move(heap);
}
+void IsolateGroup::set_saved_unlinked_calls(const Array& saved_unlinked_calls) {
+ saved_unlinked_calls_ = saved_unlinked_calls.raw();
+}
+
Thread* IsolateGroup::ScheduleThreadLocked(MonitorLocker* ml,
Thread* existing_mutator_thread,
bool is_vm_isolate,
@@ -694,6 +722,13 @@
writer.WriteMessage(msg, main_port(), Message::kOOBPriority));
}
+void Isolate::set_object_store(ObjectStore* object_store) {
+ ASSERT(cached_object_store_ == nullptr);
+ object_store_shared_ptr_.reset(object_store);
+ cached_object_store_ = object_store;
+ isolate_object_store_->set_object_store(object_store);
+}
+
class IsolateMessageHandler : public MessageHandler {
public:
explicit IsolateMessageHandler(Isolate* isolate);
@@ -1329,9 +1364,17 @@
current_tag_(UserTag::null()),
default_tag_(UserTag::null()),
ic_miss_code_(Code::null()),
- class_table_(isolate_group->class_table()),
+ shared_class_table_(isolate_group->shared_class_table()),
field_table_(new FieldTable()),
isolate_group_(isolate_group),
+ isolate_object_store_(
+ new IsolateObjectStore(isolate_group->object_store())),
+ object_store_shared_ptr_(isolate_group->object_store_shared_ptr()),
+#if defined(DART_PRECOMPILED_RUNTIME)
+ class_table_(isolate_group->class_table_shared_ptr()),
+#else
+ class_table_(new ClassTable(shared_class_table_)),
+#endif
#if !defined(DART_PRECOMPILED_RUNTIME)
native_callback_trampolines_(),
#endif
@@ -1352,7 +1395,7 @@
start_time_micros_(OS::GetCurrentMonotonicMicros()),
random_(),
mutex_(NOT_IN_PRODUCT("Isolate::mutex_")),
- symbols_mutex_(NOT_IN_PRODUCT("Isolate::symbols_mutex_")),
+ symbols_lock_(new SafepointRwLock()),
type_canonicalization_mutex_(
NOT_IN_PRODUCT("Isolate::type_canonicalization_mutex_")),
constant_canonicalization_mutex_(
@@ -1373,6 +1416,8 @@
spawn_count_monitor_(),
handler_info_cache_(),
catch_entry_moves_cache_() {
+ cached_object_store_ = object_store_shared_ptr_.get();
+ cached_class_table_table_ = class_table_->table();
FlagsCopyFrom(api_flags);
SetErrorsFatal(true);
// TODO(asiva): A Thread is not available here, need to figure out
@@ -1407,9 +1452,6 @@
delete reverse_pc_lookup_cache_;
reverse_pc_lookup_cache_ = nullptr;
- delete dispatch_table_;
- dispatch_table_ = nullptr;
-
if (FLAG_enable_interpreter) {
delete background_compiler_;
background_compiler_ = nullptr;
@@ -1428,7 +1470,6 @@
#endif // !defined(PRODUCT)
free(name_);
- delete object_store_;
delete field_table_;
#if defined(USING_SIMULATOR)
delete simulator_;
@@ -1485,6 +1526,21 @@
bool is_vm_isolate) {
Isolate* result = new Isolate(isolate_group, api_flags);
result->BuildName(name_prefix);
+ if (!is_vm_isolate) {
+ // vm isolate object store is initialized later, after null instance
+ // is created (in Dart::Init).
+ // Non-vm isolates need to have isolate object store initialized is that
+ // exit_listeners have to be null-initialized as they will be used if
+ // we fail to create isolate below, have to do low level shutdown.
+ if (result->object_store() == nullptr) {
+ // in JIT with --enable-isolate-groups each isolate still
+ // has to have its own object store
+ result->set_object_store(new ObjectStore());
+ result->object_store()->InitStubs();
+ }
+ result->isolate_object_store()->Init();
+ }
+
ASSERT(result != nullptr);
#if !defined(PRODUCT)
@@ -1608,6 +1664,17 @@
return OS::GetCurrentMonotonicMicros() - start_time_micros_;
}
+Dart_Port Isolate::origin_id() {
+ MutexLocker ml(&origin_id_mutex_);
+ return origin_id_;
+}
+
+void Isolate::set_origin_id(Dart_Port id) {
+ MutexLocker ml(&origin_id_mutex_);
+ ASSERT((id == main_port_ && origin_id_ == 0) || (origin_id_ == main_port_));
+ origin_id_ = id;
+}
+
bool Isolate::IsPaused() const {
#if defined(PRODUCT)
return false;
@@ -1664,7 +1731,7 @@
RELEASE_ASSERT(isolates_.First() == isolates_.Last());
RELEASE_ASSERT(isolates_.First() == Isolate::Current());
- auto shared_class_table = IsolateGroup::Current()->class_table();
+ auto shared_class_table = IsolateGroup::Current()->shared_class_table();
std::shared_ptr<IsolateGroupReloadContext> group_reload_context(
new IsolateGroupReloadContext(this, shared_class_table, js));
group_reload_context_ = group_reload_context;
@@ -1697,7 +1764,7 @@
RELEASE_ASSERT(isolates_.First() == isolates_.Last());
RELEASE_ASSERT(isolates_.First() == Isolate::Current());
- auto shared_class_table = IsolateGroup::Current()->class_table();
+ auto shared_class_table = IsolateGroup::Current()->shared_class_table();
std::shared_ptr<IsolateGroupReloadContext> group_reload_context(
new IsolateGroupReloadContext(this, shared_class_table, js));
group_reload_context_ = group_reload_context;
@@ -1802,7 +1869,7 @@
compiler::target::kSmiMax / (6 * kWordSize);
const GrowableObjectArray& caps = GrowableObjectArray::Handle(
- current_zone(), object_store()->resume_capabilities());
+ current_zone(), isolate_object_store()->resume_capabilities());
Capability& current = Capability::Handle(current_zone());
intptr_t insertion_index = -1;
for (intptr_t i = 0; i < caps.Length(); i++) {
@@ -1831,7 +1898,7 @@
bool Isolate::RemoveResumeCapability(const Capability& capability) {
const GrowableObjectArray& caps = GrowableObjectArray::Handle(
- current_zone(), object_store()->resume_capabilities());
+ current_zone(), isolate_object_store()->resume_capabilities());
Capability& current = Capability::Handle(current_zone());
for (intptr_t i = 0; i < caps.Length(); i++) {
current ^= caps.At(i);
@@ -1854,7 +1921,7 @@
compiler::target::kSmiMax / (12 * kWordSize);
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
- current_zone(), object_store()->exit_listeners());
+ current_zone(), isolate_object_store()->exit_listeners());
SendPort& current = SendPort::Handle(current_zone());
intptr_t insertion_index = -1;
for (intptr_t i = 0; i < listeners.Length(); i += 2) {
@@ -1885,7 +1952,7 @@
void Isolate::RemoveExitListener(const SendPort& listener) {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
- current_zone(), object_store()->exit_listeners());
+ current_zone(), isolate_object_store()->exit_listeners());
SendPort& current = SendPort::Handle(current_zone());
for (intptr_t i = 0; i < listeners.Length(); i += 2) {
current ^= listeners.At(i);
@@ -1901,7 +1968,7 @@
void Isolate::NotifyExitListeners() {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
- current_zone(), this->object_store()->exit_listeners());
+ current_zone(), isolate_object_store()->exit_listeners());
if (listeners.IsNull()) return;
SendPort& listener = SendPort::Handle(current_zone());
@@ -1922,7 +1989,7 @@
compiler::target::kSmiMax / (6 * kWordSize);
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
- current_zone(), object_store()->error_listeners());
+ current_zone(), isolate_object_store()->error_listeners());
SendPort& current = SendPort::Handle(current_zone());
intptr_t insertion_index = -1;
for (intptr_t i = 0; i < listeners.Length(); i++) {
@@ -1950,7 +2017,7 @@
void Isolate::RemoveErrorListener(const SendPort& listener) {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
- current_zone(), object_store()->error_listeners());
+ current_zone(), isolate_object_store()->error_listeners());
SendPort& current = SendPort::Handle(current_zone());
for (intptr_t i = 0; i < listeners.Length(); i++) {
current ^= listeners.At(i);
@@ -1966,7 +2033,7 @@
bool Isolate::NotifyErrorListeners(const String& msg,
const String& stacktrace) {
const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
- current_zone(), this->object_store()->error_listeners());
+ current_zone(), isolate_object_store()->error_listeners());
if (listeners.IsNull()) return false;
const Array& arr = Array::Handle(current_zone(), Array::New(2));
@@ -2388,16 +2455,23 @@
ValidationPolicy validate_frames) {
ASSERT(visitor != nullptr);
- // Visit objects in the object store.
- if (object_store() != nullptr) {
+ // Visit objects in the object store if there is no isolate group object store
+ if (group()->object_store() == nullptr && object_store() != nullptr) {
object_store()->VisitObjectPointers(visitor);
}
+ // Visit objects in the isolate object store.
+ if (isolate_object_store() != nullptr) {
+ isolate_object_store()->VisitObjectPointers(visitor);
+ }
// Visit objects in the class table.
class_table()->VisitObjectPointers(visitor);
// Visit objects in the field table.
field_table()->VisitObjectPointers(visitor);
+ if (saved_initial_field_table() != nullptr) {
+ saved_initial_field_table()->VisitObjectPointers(visitor);
+ }
visitor->clear_gc_root_type();
// Visit the objects directly referenced from the isolate structure.
@@ -2511,6 +2585,11 @@
}
}
+Isolate* IsolateGroup::FirstIsolate() const {
+ SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
+ return isolates_.IsEmpty() ? nullptr : isolates_.First();
+}
+
void IsolateGroup::RunWithStoppedMutators(
std::function<void()> single_current_mutator,
std::function<void()> otherwise,
@@ -2522,6 +2601,12 @@
return;
}
+ if (thread->IsAtSafepoint() &&
+ safepoint_handler()->IsOwnedByTheThread(thread)) {
+ single_current_mutator();
+ return;
+ }
+
{
SafepointReadRwLocker ml(thread, isolates_lock_.get());
const bool only_one_isolate = isolates_.First() == isolates_.Last();
@@ -2551,6 +2636,11 @@
},
/*at_safepoint=*/true);
api_state()->VisitObjectPointersUnlocked(visitor);
+ // Visit objects in the object store.
+ if (object_store() != nullptr) {
+ object_store()->VisitObjectPointers(visitor);
+ }
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(&saved_unlinked_calls_));
VisitStackPointers(visitor, validate_frames);
}
@@ -2624,10 +2714,10 @@
if (IsReloading()) {
return group_reload_context_->GetClassSizeForHeapWalkAt(cid);
} else {
- return class_table()->SizeAt(cid);
+ return shared_class_table()->SizeAt(cid);
}
#else
- return class_table()->SizeAt(cid);
+ return shared_class_table()->SizeAt(cid);
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
}
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 621dfd2..eaaf89c 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -19,8 +19,8 @@
#include "vm/base_isolate.h"
#include "vm/class_table.h"
#include "vm/constants_kbc.h"
+#include "vm/dispatch_table.h"
#include "vm/exceptions.h"
-#include "vm/ffi_callback_trampolines.h"
#include "vm/field_table.h"
#include "vm/fixed_cache.h"
#include "vm/growable_array.h"
@@ -37,6 +37,10 @@
#include "vm/token_position.h"
#include "vm/virtual_memory.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/ffi_callback_trampolines.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
// Forward declarations.
@@ -46,7 +50,6 @@
class CodeIndexTable;
class Debugger;
class DeoptContext;
-class DispatchTable;
class ExternalTypedData;
class HandleScope;
class HandleVisitor;
@@ -55,6 +58,7 @@
#if !defined(DART_PRECOMPILED_RUNTIME)
class Interpreter;
#endif
+class IsolateObjectStore;
class IsolateProfilerData;
class IsolateReloadContext;
class IsolateSpawnState;
@@ -275,6 +279,9 @@
// Represents an isolate group and is shared among all isolates within a group.
class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
public:
+ IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
+ void* embedder_data,
+ ObjectStore* object_store);
IsolateGroup(std::shared_ptr<IsolateGroupSource> source, void* embedder_data);
~IsolateGroup();
@@ -333,8 +340,18 @@
}
#endif // !defined(PRODUCT)
- SharedClassTable* class_table() const { return shared_class_table_.get(); }
+ DispatchTable* dispatch_table() const { return dispatch_table_.get(); }
+ void set_dispatch_table(DispatchTable* table) {
+ dispatch_table_.reset(table);
+ }
+
+ SharedClassTable* shared_class_table() const {
+ return shared_class_table_.get();
+ }
StoreBuffer* store_buffer() const { return store_buffer_.get(); }
+ ClassTable* class_table() const { return class_table_.get(); }
+ ObjectStore* object_store() const { return object_store_.get(); }
+ SafepointRwLock* symbols_lock() { return symbols_lock_.get(); }
static inline IsolateGroup* Current() {
Thread* thread = Thread::Current();
@@ -386,6 +403,7 @@
// adding/removing isolates, so no locks will be held.
void ForEachIsolate(std::function<void(Isolate* isolate)> function,
bool at_safepoint = false);
+ Isolate* FirstIsolate() const;
// Ensures mutators are stopped during execution of the provided function.
//
@@ -401,8 +419,9 @@
std::function<void()> otherwise,
bool use_force_growth_in_otherwise = false);
- void RunWithStoppedMutators(std::function<void()> function) {
- RunWithStoppedMutators(function, function);
+ void RunWithStoppedMutators(std::function<void()> function,
+ bool use_force_growth = false) {
+ RunWithStoppedMutators(function, function, use_force_growth);
}
#ifndef PRODUCT
@@ -483,9 +502,14 @@
void RememberLiveTemporaries();
void DeferredMarkLiveTemporaries();
+ RawArray* saved_unlinked_calls() const { return saved_unlinked_calls_; }
+ void set_saved_unlinked_calls(const Array& saved_unlinked_calls);
+
private:
friend class Heap;
friend class StackFrame; // For `[isolates_].First()`.
+ // For `object_store_shared_ptr()`, `class_table_shared_ptr()`
+ friend class Isolate;
#define ISOLATE_GROUP_FLAG_BITS(V) V(CompactionInProgress)
@@ -503,6 +527,13 @@
void set_heap(std::unique_ptr<Heap> value);
+ const std::shared_ptr<ClassTable>& class_table_shared_ptr() const {
+ return class_table_;
+ }
+ const std::shared_ptr<ObjectStore>& object_store_shared_ptr() const {
+ return object_store_;
+ }
+
bool is_vm_isolate_heap_ = false;
void* embedder_data_ = nullptr;
@@ -544,8 +575,17 @@
uint64_t id_ = 0;
std::unique_ptr<SharedClassTable> shared_class_table_;
+ std::shared_ptr<ObjectStore> object_store_; // nullptr in JIT mode
+ std::shared_ptr<ClassTable> class_table_; // nullptr in JIT mode
+ // This symbols_mutex_ on Isolate is only used when IsolateGroup does not
+ // have object_store.
+ std::unique_ptr<SafepointRwLock>
+ symbols_lock_; // Protects concurrent access to the symbol table.
std::unique_ptr<StoreBuffer> store_buffer_;
std::unique_ptr<Heap> heap_;
+ std::unique_ptr<DispatchTable> dispatch_table_;
+ RawArray* saved_unlinked_calls_;
+
IdleTimeHandler idle_time_handler_;
uint32_t isolate_group_flags_ = 0;
};
@@ -604,12 +644,53 @@
return group()->safepoint_handler();
}
- ClassTable* class_table() { return &class_table_; }
- static intptr_t class_table_offset() {
- return OFFSET_OF(Isolate, class_table_);
+ ClassTable* class_table() { return class_table_.get(); }
+
+ RawClass** cached_class_table_table() { return cached_class_table_table_; }
+ void set_cached_class_table_table(RawClass** cached_class_table_table) {
+ cached_class_table_table_ = cached_class_table_table;
+ }
+ static intptr_t cached_class_table_table_offset() {
+ return OFFSET_OF(Isolate, cached_class_table_table_);
}
+ SharedClassTable* shared_class_table() const { return shared_class_table_; }
+ // Used during isolate creation to re-register isolate with right group.
+ void set_shared_class_table(SharedClassTable* table) {
+ shared_class_table_ = table;
+ }
+ // Used by the generated code.
+ static intptr_t shared_class_table_offset() {
+ return OFFSET_OF(Isolate, shared_class_table_);
+ }
+
+ ObjectStore* object_store() const { return object_store_shared_ptr_.get(); }
+ void set_object_store(ObjectStore* object_store);
+ static intptr_t cached_object_store_offset() {
+ return OFFSET_OF(Isolate, cached_object_store_);
+ }
+ SafepointRwLock* symbols_lock() { return symbols_lock_.get(); }
+
FieldTable* field_table() const { return field_table_; }
+ void set_field_table(Thread* T, FieldTable* field_table) {
+ delete field_table_;
+ field_table_ = field_table;
+ T->field_table_values_ = field_table->table();
+ }
+
+ FieldTable* saved_initial_field_table() const {
+ return saved_initial_field_table_.get();
+ }
+ std::shared_ptr<FieldTable> saved_initial_field_table_shareable() {
+ return saved_initial_field_table_;
+ }
+ void set_saved_initial_field_table(std::shared_ptr<FieldTable> field_table) {
+ saved_initial_field_table_ = field_table;
+ }
+
+ IsolateObjectStore* isolate_object_store() const {
+ return isolate_object_store_.get();
+ }
// Prefers old classes when we are in the middle of a reload.
RawClass* GetClassForHeapWalkAt(intptr_t cid);
@@ -643,11 +724,8 @@
ASSERT(main_port_ == 0); // Only set main port once.
main_port_ = port;
}
- Dart_Port origin_id() const { return origin_id_; }
- void set_origin_id(Dart_Port id) {
- ASSERT((id == main_port_ && origin_id_ == 0) || (origin_id_ == main_port_));
- origin_id_ = id;
- }
+ Dart_Port origin_id();
+ void set_origin_id(Dart_Port id);
void set_pause_capability(uint64_t value) { pause_capability_ = value; }
uint64_t pause_capability() const { return pause_capability_; }
void set_terminate_capability(uint64_t value) {
@@ -659,12 +737,6 @@
Heap* heap() const { return isolate_group_->heap(); }
- ObjectStore* object_store() const { return object_store_; }
- void set_object_store(ObjectStore* value) { object_store_ = value; }
- static intptr_t object_store_offset() {
- return OFFSET_OF(Isolate, object_store_);
- }
-
void set_init_callback_data(void* value) { init_callback_data_ = value; }
void* init_callback_data() const { return init_callback_data_; }
@@ -714,7 +786,6 @@
}
Mutex* mutex() { return &mutex_; }
- Mutex* symbols_mutex() { return &symbols_mutex_; }
Mutex* type_canonicalization_mutex() { return &type_canonicalization_mutex_; }
Mutex* constant_canonicalization_mutex() {
return &constant_canonicalization_mutex_;
@@ -732,7 +803,6 @@
#if !defined(PRODUCT)
Debugger* debugger() const {
- ASSERT(debugger_ != nullptr);
return debugger_;
}
@@ -1045,8 +1115,9 @@
void set_obfuscation_map(const char** map) { obfuscation_map_ = map; }
const char** obfuscation_map() const { return obfuscation_map_; }
- const DispatchTable* dispatch_table() const { return dispatch_table_; }
- void set_dispatch_table(DispatchTable* table) { dispatch_table_ = table; }
+ const DispatchTable* dispatch_table() const {
+ return group()->dispatch_table();
+ }
// Returns the pc -> code lookup cache object for this isolate.
ReversePcLookupCache* reverse_pc_lookup_cache() const {
@@ -1230,14 +1301,23 @@
RawUserTag* current_tag_;
RawUserTag* default_tag_;
RawCode* ic_miss_code_;
- ObjectStore* object_store_ = nullptr;
- ClassTable class_table_;
+ // Cached value of object_store_shared_ptr_, here for generated code access
+ ObjectStore* cached_object_store_ = nullptr;
+ SharedClassTable* shared_class_table_ = nullptr;
+ // Cached value of class_table_->table_, here for generated code access
+ RawClass** cached_class_table_table_ = nullptr;
FieldTable* field_table_ = nullptr;
bool single_step_ = false;
// End accessed from generated code.
IsolateGroup* isolate_group_;
IdleTimeHandler idle_time_handler_;
+ std::shared_ptr<FieldTable> saved_initial_field_table_;
+ std::unique_ptr<IsolateObjectStore> isolate_object_store_;
+ // shared in AOT(same pointer as on IsolateGroup), not shared in JIT
+ std::shared_ptr<ObjectStore> object_store_shared_ptr_;
+ // shared in AOT(same pointer as on IsolateGroup), not shared in JIT
+ std::shared_ptr<ClassTable> class_table_;
#if !defined(DART_PRECOMPILED_RUNTIME)
NativeCallbackTrampolines native_callback_trampolines_;
@@ -1325,6 +1405,7 @@
Dart_Port main_port_ = 0;
// Isolates created by Isolate.spawn have the same origin id.
Dart_Port origin_id_ = 0;
+ Mutex origin_id_mutex_;
uint64_t pause_capability_ = 0;
uint64_t terminate_capability_ = 0;
void* init_callback_data_ = nullptr;
@@ -1332,7 +1413,8 @@
Random random_;
Simulator* simulator_ = nullptr;
Mutex mutex_; // Protects compiler stats.
- Mutex symbols_mutex_; // Protects concurrent access to the symbol table.
+ std::unique_ptr<SafepointRwLock>
+ symbols_lock_; // Protects concurrent access to the symbol table.
Mutex type_canonicalization_mutex_; // Protects type canonicalization.
Mutex constant_canonicalization_mutex_; // Protects const canonicalization.
Mutex megamorphic_mutex_; // Protects the table of megamorphic caches and
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index 1c1c752..5e69eac 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -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.
+#if !defined(DART_PRECOMPILED_RUNTIME)
+
#include "vm/kernel.h"
#include "vm/bit_vector.h"
@@ -14,7 +16,6 @@
#include "vm/parser.h" // For Parser::kParameter* constants.
#include "vm/stack_frame.h"
-#if !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
namespace kernel {
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index b36b2a9..754d2e5 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1,6 +1,7 @@
// Copyright (c) 2016, 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.
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/kernel_loader.h"
@@ -23,7 +24,6 @@
#include "vm/symbols.h"
#include "vm/thread.h"
-#if !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
namespace kernel {
diff --git a/runtime/vm/native_entry.cc b/runtime/vm/native_entry.cc
index 3690222..47582a1 100644
--- a/runtime/vm/native_entry.cc
+++ b/runtime/vm/native_entry.cc
@@ -310,9 +310,14 @@
const Code& current_trampoline =
Code::Handle(zone, CodePatcher::GetNativeCallAt(
caller_frame->pc(), code, ¤t_function));
+ // Some other isolate(with code being shared in AOT) might have updated
+ // target function/trampoline already.
ASSERT(current_function ==
- reinterpret_cast<NativeFunction>(LinkNativeCall));
- ASSERT(current_trampoline.raw() == StubCode::CallBootstrapNative().raw());
+ reinterpret_cast<NativeFunction>(LinkNativeCall) ||
+ current_function == target_function);
+ ASSERT(current_trampoline.raw() ==
+ StubCode::CallBootstrapNative().raw() ||
+ current_function == target_function);
}
#endif
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 6df21cb..aac5f8b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -13,17 +13,10 @@
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/code_comments.h"
+#include "vm/code_descriptors.h"
#include "vm/code_observers.h"
-#include "vm/compiler/aot/precompiler.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
-#include "vm/compiler/compiler_state.h"
-#include "vm/compiler/frontend/bytecode_fingerprints.h"
-#include "vm/compiler/frontend/bytecode_reader.h"
-#include "vm/compiler/frontend/kernel_fingerprints.h"
-#include "vm/compiler/frontend/kernel_translation_helper.h"
-#include "vm/compiler/intrinsifier.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/cpu.h"
#include "vm/dart.h"
@@ -63,6 +56,18 @@
#include "vm/type_testing_stubs.h"
#include "vm/zone_text_buffer.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/aot/precompiler.h"
+#include "vm/compiler/assembler/assembler.h"
+#include "vm/compiler/backend/code_statistics.h"
+#include "vm/compiler/compiler_state.h"
+#include "vm/compiler/frontend/bytecode_fingerprints.h"
+#include "vm/compiler/frontend/bytecode_reader.h"
+#include "vm/compiler/frontend/kernel_fingerprints.h"
+#include "vm/compiler/frontend/kernel_translation_helper.h"
+#include "vm/compiler/intrinsifier.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
DEFINE_FLAG(int,
@@ -1631,6 +1636,8 @@
#if !defined(DART_PRECOMPILED_RUNTIME)
// Object::Init version when we are bootstrapping from source or from a
// Kernel binary.
+ // This will initialize isolate group object_store, shared by all isolates
+ // running in the isolate group.
ObjectStore* object_store = isolate->object_store();
Class& cls = Class::Handle(zone);
@@ -1642,6 +1649,7 @@
// All RawArray fields will be initialized to an empty array, therefore
// initialize array class first.
cls = Class::New<Array, RTN::Array>(isolate);
+ ASSERT(object_store->array_class() == Class::null());
object_store->set_array_class(cls);
// VM classes that are parameterized (Array, ImmutableArray,
@@ -2530,8 +2538,7 @@
uword cur = address + sizeof(RawObject);
uword end = address + size;
if (class_id == kInstructionsCid) {
- compiler::target::uword initial_value =
- compiler::Assembler::GetBreakInstructionFiller();
+ compiler::target::uword initial_value = kBreakInstructionFiller;
while (cur < end) {
*reinterpret_cast<compiler::target::uword*>(cur) = initial_value;
cur += compiler::target::kWordSize;
@@ -2630,7 +2637,7 @@
}
}
#ifndef PRODUCT
- auto class_table = thread->isolate_group()->class_table();
+ auto class_table = thread->isolate_group()->shared_class_table();
if (class_table->TraceAllocationFor(cls_id)) {
Profiler::SampleAllocation(thread, cls_id);
}
@@ -3287,9 +3294,10 @@
set_num_native_fields(super.num_native_fields());
if (FLAG_precompiled_mode) {
- host_bitmap =
- Isolate::Current()->group()->class_table()->GetUnboxedFieldsMapAt(
- super.id());
+ host_bitmap = Isolate::Current()
+ ->group()
+ ->shared_class_table()
+ ->GetUnboxedFieldsMapAt(super.id());
}
}
// If the super class is parameterized, use the same type_arguments field,
@@ -3752,8 +3760,8 @@
// Sets the new size in the class table.
isolate->class_table()->SetAt(id(), raw());
if (FLAG_precompiled_mode) {
- isolate->group()->class_table()->SetUnboxedFieldsMapAt(id(),
- host_bitmap);
+ isolate->group()->shared_class_table()->SetUnboxedFieldsMapAt(
+ id(), host_bitmap);
}
}
}
@@ -3848,7 +3856,7 @@
bool Class::TraceAllocation(Isolate* isolate) const {
#ifndef PRODUCT
- auto class_table = isolate->group()->class_table();
+ auto class_table = isolate->group()->shared_class_table();
return class_table->TraceAllocationFor(id());
#else
return false;
@@ -3860,7 +3868,7 @@
Isolate* isolate = Isolate::Current();
const bool changed = trace_allocation != this->TraceAllocation(isolate);
if (changed) {
- auto class_table = isolate->group()->class_table();
+ auto class_table = isolate->group()->shared_class_table();
class_table->SetTraceAllocationFor(id(), trace_allocation);
DisableAllocationStub();
}
@@ -7469,6 +7477,7 @@
}
}
+#if !defined(DART_PRECOMPILED_RUNTIME)
bool Function::CanBeInlined() const {
// Our force-optimized functions cannot deoptimize to an unoptimized frame.
// If the instructions of the force-optimized function body get moved via
@@ -7479,14 +7488,17 @@
if (ForceOptimize()) {
return CompilerState::Current().is_aot();
}
-#if defined(PRODUCT)
- return is_inlinable() && !is_external() && !is_generated_body();
-#else
+
+#if !defined(PRODUCT)
Thread* thread = Thread::Current();
- return is_inlinable() && !is_external() && !is_generated_body() &&
- !thread->isolate()->debugger()->HasBreakpoint(*this, thread->zone());
-#endif
+ if (thread->isolate()->debugger()->HasBreakpoint(*this, thread->zone())) {
+ return false;
+ }
+#endif // !defined(PRODUCT)
+
+ return is_inlinable() && !is_external() && !is_generated_body();
}
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
intptr_t Function::NumParameters() const {
return num_fixed_parameters() + NumOptionalParameters();
@@ -17545,7 +17557,7 @@
Instance& member = Instance::Handle();
const auto unboxed_fields_bitmap =
- thread->isolate()->group()->class_table()->GetUnboxedFieldsMapAt(
+ thread->isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
GetClassId());
for (intptr_t offset = Instance::NextFieldOffset(); offset < instance_size;
@@ -17597,7 +17609,7 @@
const intptr_t instance_size = SizeFromClass();
ASSERT(instance_size != 0);
const auto unboxed_fields_bitmap =
- thread->isolate()->group()->class_table()->GetUnboxedFieldsMapAt(
+ thread->isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
GetClassId());
for (intptr_t offset = Instance::NextFieldOffset(); offset < instance_size;
offset += kWordSize) {
@@ -23529,25 +23541,66 @@
return result.raw();
}
+#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
+static void PrintStackTraceFrameBodyFromDSO(ZoneTextBuffer* buffer,
+ uword call_addr,
+ bool print_virtual_address) {
+ uword dso_base;
+ char* dso_name;
+ if (NativeSymbolResolver::LookupSharedObject(call_addr, &dso_base,
+ &dso_name)) {
+ uword dso_offset = call_addr - dso_base;
+ if (print_virtual_address) {
+ buffer->Printf(" virt %" Pp "", dso_offset);
+ }
+ uword symbol_start;
+ if (auto const symbol_name =
+ NativeSymbolResolver::LookupSymbolName(call_addr, &symbol_start)) {
+ uword symbol_offset = call_addr - symbol_start;
+ buffer->Printf(" %s+0x%" Px "", symbol_name, symbol_offset);
+ NativeSymbolResolver::FreeSymbolName(symbol_name);
+ } else {
+ buffer->Printf(" %s", dso_name);
+ }
+ NativeSymbolResolver::FreeSymbolName(dso_name);
+ } else {
+ buffer->Printf(" <unknown>");
+ }
+ buffer->Printf("\n");
+}
+#endif
+
+static void PrintStackTraceFrameIndex(ZoneTextBuffer* buffer,
+ intptr_t frame_index) {
+ buffer->Printf("#%-6" Pd "", frame_index);
+}
+
+static void PrintStackTraceFrameBody(ZoneTextBuffer* buffer,
+ const char* function_name,
+ const char* url,
+ intptr_t line = -1,
+ intptr_t column = -1) {
+ buffer->Printf(" %s (%s", function_name, url);
+ if (line >= 0) {
+ buffer->Printf(":%" Pd "", line);
+ if (column >= 0) {
+ buffer->Printf(":%" Pd "", column);
+ }
+ }
+ buffer->Printf(")\n");
+}
+
static void PrintStackTraceFrame(Zone* zone,
ZoneTextBuffer* buffer,
const Function& function,
TokenPosition token_pos,
intptr_t frame_index) {
- auto& script = Script::Handle(zone);
- const char* function_name;
- const char* url;
-
- if (!function.IsNull()) {
- script = function.script();
- auto& handle = String::Handle(zone, function.QualifiedUserVisibleName());
- function_name = handle.ToCString();
- handle = script.IsNull() ? String::New("Kernel") : script.url();
- url = handle.ToCString();
- } else {
- function_name = Symbols::OptimizedOut().ToCString();
- url = function_name;
- }
+ ASSERT(!function.IsNull());
+ const auto& script = Script::Handle(zone, function.script());
+ auto& handle = String::Handle(zone, function.QualifiedUserVisibleName());
+ auto const function_name = handle.ToCString();
+ handle = script.IsNull() ? String::New("Kernel") : script.url();
+ auto url = handle.ToCString();
// If the URI starts with "data:application/dart;" this is a URI encoded
// script so we shouldn't print the entire URI because it could be very long.
@@ -23559,30 +23612,13 @@
intptr_t column = -1;
if (FLAG_precompiled_mode) {
line = token_pos.value();
- } else if (!script.IsNull() && token_pos.IsSourcePosition()) {
+ } else if (token_pos.IsSourcePosition()) {
+ ASSERT(!script.IsNull());
script.GetTokenLocation(token_pos.SourcePosition(), &line, &column);
}
- buffer->Printf("#%-6" Pd " %s (%s", frame_index, function_name, url);
- if (line >= 0) {
- buffer->Printf(":%" Pd "", line);
- if (column >= 0) {
- buffer->Printf(":%" Pd "", column);
- }
- }
- buffer->Printf(")\n");
-}
-
-static inline bool ShouldPrintFrame(const Function& function) {
- // TODO(dartbug.com/41052): Currently, we print frames where the function
- // object was optimized out in the precompiled runtime, even if the original
- // function was not visible. We may want to either elide such frames, or
- // instead store additional information in the WSR that allows us to determine
- // the original visibility.
-#if defined(DART_PRECOMPILED_RUNTIME)
- if (function.IsNull()) return true;
-#endif
- return FLAG_show_invisible_frames || function.is_visible();
+ PrintStackTraceFrameIndex(buffer, frame_index);
+ PrintStackTraceFrameBody(buffer, function_name, url, line, column);
}
const char* StackTrace::ToDartCString(const StackTrace& stack_trace_in) {
@@ -23621,32 +23657,39 @@
if (code_object.IsCode()) {
code ^= code_object.raw();
ASSERT(code.IsFunctionCode());
- if (code.is_optimized() && stack_trace.expand_inlined()) {
+ function = code.function();
+ const uword pc = code.PayloadStart() + pc_offset;
+ if (function.IsNull()) {
+#if defined(DART_PRECOMPILED_RUNTIME)
+ PrintStackTraceFrameIndex(&buffer, frame_index);
+ PrintStackTraceFrameBodyFromDSO(&buffer, pc - 1,
+ /*print_virtual_address=*/false);
+ frame_index++;
+#else
+ UNREACHABLE();
+#endif
+ } else if (code.is_optimized() && stack_trace.expand_inlined()) {
code.GetInlinedFunctionsAtReturnAddress(
pc_offset, &inlined_functions, &inlined_token_positions);
ASSERT(inlined_functions.length() >= 1);
for (intptr_t j = inlined_functions.length() - 1; j >= 0; j--) {
const auto& inlined = *inlined_functions[j];
auto const pos = inlined_token_positions[j];
- if (ShouldPrintFrame(inlined)) {
+ if (FLAG_show_invisible_frames || function.is_visible()) {
PrintStackTraceFrame(zone, &buffer, inlined, pos, frame_index);
frame_index++;
}
}
- } else {
- function = code.function();
- if (ShouldPrintFrame(function)) {
- uword pc = code.PayloadStart() + pc_offset;
- auto const pos = code.GetTokenIndexOfPC(pc);
- PrintStackTraceFrame(zone, &buffer, function, pos, frame_index);
- frame_index++;
- }
+ } else if (FLAG_show_invisible_frames || function.is_visible()) {
+ auto const pos = code.GetTokenIndexOfPC(pc);
+ PrintStackTraceFrame(zone, &buffer, function, pos, frame_index);
+ frame_index++;
}
} else {
ASSERT(code_object.IsBytecode());
bytecode ^= code_object.raw();
function = bytecode.function();
- if (ShouldPrintFrame(function)) {
+ if (FLAG_show_invisible_frames || function.is_visible()) {
uword pc = bytecode.PayloadStart() + pc_offset;
auto const pos = bytecode.GetTokenIndexOfPC(pc);
PrintStackTraceFrame(zone, &buffer, function, pos, frame_index);
@@ -23724,26 +23767,8 @@
uword return_addr = start + pc_offset;
uword call_addr = return_addr - 1;
buffer.Printf(" #%02" Pd " abs %" Pp "", frame_index, call_addr);
- uword dso_base;
- char* dso_name;
- if (NativeSymbolResolver::LookupSharedObject(call_addr, &dso_base,
- &dso_name)) {
- uword dso_offset = call_addr - dso_base;
- buffer.Printf(" virt %" Pp "", dso_offset);
- uword symbol_start;
- if (auto const symbol_name = NativeSymbolResolver::LookupSymbolName(
- call_addr, &symbol_start)) {
- uword symbol_offset = call_addr - symbol_start;
- buffer.Printf(" %s+0x%" Px "", symbol_name, symbol_offset);
- NativeSymbolResolver::FreeSymbolName(symbol_name);
- } else {
- buffer.Printf(" %s", dso_name);
- }
- NativeSymbolResolver::FreeSymbolName(dso_name);
- } else {
- buffer.Printf(" <unknown>");
- }
- buffer.Printf("\n");
+ PrintStackTraceFrameBodyFromDSO(&buffer, call_addr,
+ /*print_virtual_address=*/true);
frame_index++;
}
}
@@ -23761,9 +23786,27 @@
#endif // defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
}
+static void DwarfStackTracesHandler(bool value) {
+ FLAG_dwarf_stack_traces_mode = value;
+
+#if defined(PRODUCT)
+ // We can safely remove function objects in precompiled snapshots if the
+ // runtime will generate DWARF stack traces and we don't have runtime
+ // debugging options like the observatory available.
+ if (value) {
+ FLAG_retain_function_objects = false;
+ }
+#endif
+}
+
+DEFINE_FLAG_HANDLER(DwarfStackTracesHandler,
+ dwarf_stack_traces,
+ "Emit DWARF line number and inlining info in dylib "
+ "snapshots and don't symbolize stack traces.");
+
const char* StackTrace::ToCString() const {
#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
- if (FLAG_dwarf_stack_traces) {
+ if (FLAG_dwarf_stack_traces_mode) {
return ToDwarfCString(*this);
}
#endif
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index b3c70c1..9bbb011 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -848,7 +848,8 @@
// Make sure the declaration types argument count matches for the two classes.
// ex. class A<int,B> {} cannot be replace with class A<B> {}.
auto group_context = context->group_reload_context();
- auto shared_class_table = group_context->isolate_group()->class_table();
+ auto shared_class_table =
+ group_context->isolate_group()->shared_class_table();
if (NumTypeArguments() != replacement.NumTypeArguments()) {
group_context->AddReasonForCancelling(
new (context->zone())
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 1b6f035..595a724 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -16,6 +16,91 @@
namespace dart {
+IsolateObjectStore::IsolateObjectStore(ObjectStore* object_store)
+ : object_store_(object_store) {}
+
+IsolateObjectStore::~IsolateObjectStore() {}
+
+void IsolateObjectStore::VisitObjectPointers(ObjectPointerVisitor* visitor) {
+ ASSERT(visitor != NULL);
+ visitor->set_gc_root_type("isolate_object store");
+ visitor->VisitPointers(from(), to());
+ visitor->clear_gc_root_type();
+}
+
+void IsolateObjectStore::Init() {
+#define INIT_FIELD(Type, name) name##_ = Type::null();
+ ISOLATE_OBJECT_STORE_FIELD_LIST(INIT_FIELD, INIT_FIELD)
+#undef INIT_FIELD
+
+ for (RawObject** current = from(); current <= to(); current++) {
+ ASSERT(*current == Object::null());
+ }
+}
+
+#ifndef PRODUCT
+void IsolateObjectStore::PrintToJSONObject(JSONObject* jsobj) {
+ jsobj->AddProperty("type", "_IsolateObjectStore");
+
+ {
+ JSONObject fields(jsobj, "fields");
+ Object& value = Object::Handle();
+#define PRINT_OBJECT_STORE_FIELD(type, name) \
+ value = name##_; \
+ fields.AddProperty(#name "_", value);
+ ISOLATE_OBJECT_STORE_FIELD_LIST(PRINT_OBJECT_STORE_FIELD,
+ PRINT_OBJECT_STORE_FIELD);
+#undef PRINT_OBJECT_STORE_FIELD
+ }
+}
+#endif // !PRODUCT
+
+static RawUnhandledException* CreatePreallocatedUnandledException(
+ Zone* zone,
+ const Object& out_of_memory) {
+ // Allocate pre-allocated unhandled exception object initialized with the
+ // pre-allocated OutOfMemoryError.
+ const UnhandledException& unhandled_exception =
+ UnhandledException::Handle(UnhandledException::New(
+ Instance::Cast(out_of_memory), StackTrace::Handle(zone)));
+ return unhandled_exception.raw();
+}
+
+static RawStackTrace* CreatePreallocatedStackTrace(Zone* zone) {
+ const Array& code_array = Array::Handle(
+ zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
+ const Array& pc_offset_array = Array::Handle(
+ zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
+ const StackTrace& stack_trace =
+ StackTrace::Handle(zone, StackTrace::New(code_array, pc_offset_array));
+ // Expansion of inlined functions requires additional memory at run time,
+ // avoid it.
+ stack_trace.set_expand_inlined(false);
+ return stack_trace.raw();
+}
+
+RawError* IsolateObjectStore::PreallocateObjects() {
+ Thread* thread = Thread::Current();
+ Isolate* isolate = thread->isolate();
+ Zone* zone = thread->zone();
+ ASSERT(isolate != NULL && isolate->isolate_object_store() == this);
+ ASSERT(preallocated_stack_trace() == StackTrace::null());
+ resume_capabilities_ = GrowableObjectArray::New();
+ exit_listeners_ = GrowableObjectArray::New();
+ error_listeners_ = GrowableObjectArray::New();
+
+ // Allocate pre-allocated unhandled exception object initialized with the
+ // pre-allocated OutOfMemoryError.
+ const Object& out_of_memory =
+ Object::Handle(zone, object_store_->out_of_memory());
+ set_preallocated_unhandled_exception(UnhandledException::Handle(
+ CreatePreallocatedUnandledException(zone, out_of_memory)));
+ set_preallocated_stack_trace(
+ StackTrace::Handle(CreatePreallocatedStackTrace(zone)));
+
+ return Error::null();
+}
+
ObjectStore::ObjectStore() {
#define INIT_FIELD(Type, name) name##_ = Type::null();
OBJECT_STORE_FIELD_LIST(INIT_FIELD, INIT_FIELD)
@@ -35,16 +120,10 @@
visitor->clear_gc_root_type();
}
-void ObjectStore::Init(Isolate* isolate) {
- ASSERT(isolate->object_store() == NULL);
- ObjectStore* store = new ObjectStore();
- isolate->set_object_store(store);
-
- if (!Dart::VmIsolateNameEquals(isolate->name())) {
-#define DO(member, name) store->set_##member(StubCode::name());
- OBJECT_STORE_STUB_CODE_LIST(DO)
+void ObjectStore::InitStubs() {
+#define DO(member, name) set_##member(StubCode::name());
+ OBJECT_STORE_STUB_CODE_LIST(DO)
#undef DO
- }
}
#ifndef PRODUCT
@@ -72,22 +151,22 @@
RawError* ObjectStore::PreallocateObjects() {
Thread* thread = Thread::Current();
+ IsolateGroup* isolate_group = thread->isolate_group();
Isolate* isolate = thread->isolate();
- Zone* zone = thread->zone();
- ASSERT(isolate != NULL && isolate->object_store() == this);
+ // Either we are the object store on isolate group, or isolate group has no
+ // object store and we are the object store on the isolate.
+ ASSERT(isolate_group != NULL && (isolate_group->object_store() == this ||
+ (isolate_group->object_store() == nullptr &&
+ isolate->object_store() == this)));
+
if (this->stack_overflow() != Instance::null()) {
ASSERT(this->out_of_memory() != Instance::null());
- ASSERT(this->preallocated_stack_trace() != StackTrace::null());
return Error::null();
}
ASSERT(this->stack_overflow() == Instance::null());
ASSERT(this->out_of_memory() == Instance::null());
- ASSERT(this->preallocated_stack_trace() == StackTrace::null());
this->closure_functions_ = GrowableObjectArray::New();
- this->resume_capabilities_ = GrowableObjectArray::New();
- this->exit_listeners_ = GrowableObjectArray::New();
- this->error_listeners_ = GrowableObjectArray::New();
Object& result = Object::Handle();
const Library& library = Library::Handle(Library::CoreLibrary());
@@ -104,24 +183,6 @@
}
set_out_of_memory(Instance::Cast(result));
- // Allocate pre-allocated unhandled exception object initialized with the
- // pre-allocated OutOfMemoryError.
- const UnhandledException& unhandled_exception =
- UnhandledException::Handle(UnhandledException::New(
- Instance::Cast(result), StackTrace::Handle(zone)));
- set_preallocated_unhandled_exception(unhandled_exception);
-
- const Array& code_array = Array::Handle(
- zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
- const Array& pc_offset_array = Array::Handle(
- zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld));
- const StackTrace& stack_trace =
- StackTrace::Handle(zone, StackTrace::New(code_array, pc_offset_array));
- // Expansion of inlined functions requires additional memory at run time,
- // avoid it.
- stack_trace.set_expand_inlined(false);
- set_preallocated_stack_trace(stack_trace);
-
return Error::null();
}
@@ -272,10 +333,4 @@
#endif
}
-void ObjectStore::PostLoad() {
- resume_capabilities_ = GrowableObjectArray::New();
- exit_listeners_ = GrowableObjectArray::New();
- error_listeners_ = GrowableObjectArray::New();
-}
-
} // namespace dart
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 5a8640c..ecffe42 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -34,6 +34,9 @@
// TODO(liama): Once NNBD is enabled, *_type will be deleted and all uses will
// be replaced with *_type_non_nullable. Later, once we drop support for opted
// out code, *_type_legacy will be deleted.
+//
+// R_ - needs getter only
+// RW - needs getter and setter
#define OBJECT_STORE_FIELD_LIST(R_, RW) \
RW(Class, object_class) \
RW(Type, object_type) \
@@ -149,8 +152,6 @@
RW(GrowableObjectArray, pending_classes) \
RW(Instance, stack_overflow) \
RW(Instance, out_of_memory) \
- RW(UnhandledException, preallocated_unhandled_exception) \
- RW(StackTrace, preallocated_stack_trace) \
RW(Function, lookup_port_handler) \
RW(Function, handle_message_function) \
RW(Function, growable_list_factory) \
@@ -163,7 +164,6 @@
RW(Function, complete_on_async_return) \
RW(Class, async_star_stream_controller) \
RW(Array, bytecode_attributes) \
- RW(Array, saved_unlinked_calls) \
RW(GrowableObjectArray, llvm_constant_pool) \
RW(GrowableObjectArray, llvm_function_pool) \
RW(Array, llvm_constant_hash_table) \
@@ -198,12 +198,10 @@
RW(Code, call_closure_no_such_method_stub) \
R_(Code, megamorphic_call_miss_code) \
R_(Function, megamorphic_call_miss_function) \
- R_(GrowableObjectArray, resume_capabilities) \
- R_(GrowableObjectArray, exit_listeners) \
- R_(GrowableObjectArray, error_listeners) \
RW(Array, dispatch_table_code_entries) \
RW(Array, code_order_table) \
RW(Array, obfuscation_map) \
+ RW(Array, saved_initial_field_values) \
RW(Class, ffi_pointer_class) \
RW(Class, ffi_native_type_class) \
RW(Class, ffi_struct_class) \
@@ -238,8 +236,80 @@
DO(init_static_field_stub, InitStaticField) \
DO(instance_of_stub, InstanceOf)
-// The object store is a per isolate instance which stores references to
-// objects used by the VM.
+#define ISOLATE_OBJECT_STORE_FIELD_LIST(R_, RW) \
+ RW(UnhandledException, preallocated_unhandled_exception) \
+ RW(StackTrace, preallocated_stack_trace) \
+ R_(GrowableObjectArray, resume_capabilities) \
+ R_(GrowableObjectArray, exit_listeners) \
+ R_(GrowableObjectArray, error_listeners)
+// Please remember the last entry must be referred in the 'to' function below.
+
+class IsolateObjectStore {
+ public:
+ explicit IsolateObjectStore(ObjectStore* object_store);
+ ~IsolateObjectStore();
+
+#define DECLARE_GETTER(Type, name) \
+ Raw##Type* name() const { return name##_; } \
+ static intptr_t name##_offset() { \
+ return OFFSET_OF(IsolateObjectStore, name##_); \
+ }
+
+#define DECLARE_GETTER_AND_SETTER(Type, name) \
+ DECLARE_GETTER(Type, name) \
+ void set_##name(const Type& value) { name##_ = value.raw(); }
+ ISOLATE_OBJECT_STORE_FIELD_LIST(DECLARE_GETTER, DECLARE_GETTER_AND_SETTER)
+#undef DECLARE_GETTER
+#undef DECLARE_GETTER_AND_SETTER
+
+ // Visit all object pointers.
+ void VisitObjectPointers(ObjectPointerVisitor* visitor);
+
+ // Called to initialize objects required by the vm but which invoke
+ // dart code. If an error occurs the error object is returned otherwise
+ // a null object is returned.
+ RawError* PreallocateObjects();
+
+ void Init();
+ void PostLoad();
+
+ ObjectStore* object_store() const { return object_store_; }
+ void set_object_store(ObjectStore* object_store) {
+ ASSERT(object_store_ == nullptr);
+ object_store_ = object_store;
+ }
+
+ static intptr_t object_store_offset() {
+ return OFFSET_OF(IsolateObjectStore, object_store_);
+ }
+
+#ifndef PRODUCT
+ void PrintToJSONObject(JSONObject* jsobj);
+#endif
+
+ private:
+ // Finds a core library private method in Object.
+ RawFunction* PrivateObjectLookup(const String& name);
+
+ RawObject** from() {
+ return reinterpret_cast<RawObject**>(&preallocated_unhandled_exception_);
+ }
+#define DECLARE_OBJECT_STORE_FIELD(type, name) Raw##type* name##_;
+ ISOLATE_OBJECT_STORE_FIELD_LIST(DECLARE_OBJECT_STORE_FIELD,
+ DECLARE_OBJECT_STORE_FIELD)
+#undef DECLARE_OBJECT_STORE_FIELD
+ RawObject** to() { return reinterpret_cast<RawObject**>(&error_listeners_); }
+
+ ObjectStore* object_store_;
+
+ friend class Serializer;
+ friend class Deserializer;
+
+ DISALLOW_COPY_AND_ASSIGN(IsolateObjectStore);
+};
+
+// The object store is a per isolate group instance which stores references to
+// objects used by the VM shared by all isolates in a group.
class ObjectStore {
public:
enum BootstrapLibraryId {
@@ -249,6 +319,7 @@
#undef MAKE_ID
};
+ ObjectStore();
~ObjectStore();
#define DECLARE_GETTER(Type, name) \
@@ -306,17 +377,13 @@
void InitKnownObjects();
- void PostLoad();
-
- static void Init(Isolate* isolate);
+ void InitStubs();
#ifndef PRODUCT
void PrintToJSONObject(JSONObject* jsobj);
#endif
private:
- ObjectStore();
-
// Finds a core library private method in Object.
RawFunction* PrivateObjectLookup(const String& name);
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index 93c239a..b6f364f 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -13,101 +13,199 @@
namespace dart {
-class ProgramWalker : public CodeVisitor {
+class WorklistElement : public ZoneAllocated {
public:
- ProgramWalker(Zone* zone, ClassVisitor* visitor)
- : visitor_(visitor),
- object_(Object::Handle(zone)),
- fields_(Array::Handle(zone)),
- field_(Field::Handle(zone)),
- functions_(Array::Handle(zone)),
- function_(Function::Handle(zone)),
- code_(Code::Handle(zone)) {}
+ WorklistElement(Zone* zone, const Object& object)
+ : object_(Object::Handle(zone, object.raw())), next_(nullptr) {}
+ RawObject* value() const { return object_.raw(); }
+
+ void set_next(WorklistElement* elem) { next_ = elem; }
+ WorklistElement* next() const { return next_; }
+
+ private:
+ const Object& object_;
+ WorklistElement* next_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorklistElement);
+};
+
+// Implements a FIFO queue, using IsEmpty, Add, Remove operations.
+class Worklist : public ValueObject {
+ public:
+ explicit Worklist(Zone* zone)
+ : zone_(zone), first_(nullptr), last_(nullptr) {}
+
+ bool IsEmpty() const { return first_ == nullptr; }
+
+ void Add(const Object& value) {
+ auto element = new (zone_) WorklistElement(zone_, value);
+ if (first_ == nullptr) {
+ first_ = element;
+ ASSERT(last_ == nullptr);
+ } else {
+ ASSERT(last_ != nullptr);
+ last_->set_next(element);
+ }
+ last_ = element;
+ ASSERT(first_ != nullptr && last_ != nullptr);
+ }
+
+ RawObject* Remove() {
+ ASSERT(first_ != nullptr);
+ WorklistElement* result = first_;
+ first_ = first_->next();
+ if (first_ == nullptr) {
+ last_ = nullptr;
+ }
+ return result->value();
+ }
+
+ private:
+ Zone* const zone_;
+ WorklistElement* first_;
+ WorklistElement* last_;
+
+ DISALLOW_COPY_AND_ASSIGN(Worklist);
+};
+
+// Walks through the classes, functions, and code for the current program.
+//
+// Uses the heap object ID table to determine whether or not a given object
+// has been visited already.
+class ProgramWalker : public ValueObject {
+ public:
+ ProgramWalker(Zone* zone, Heap* heap, ClassVisitor* visitor)
+ : heap_(heap),
+ visitor_(visitor),
+ worklist_(zone),
+ class_object_(Object::Handle(zone)),
+ class_fields_(Array::Handle(zone)),
+ class_field_(Field::Handle(zone)),
+ class_functions_(Array::Handle(zone)),
+ class_function_(Function::Handle(zone)),
+ class_code_(Code::Handle(zone)),
+ function_code_(Code::Handle(zone)),
+ static_calls_array_(Array::Handle(zone)),
+ static_call_code_(Code::Handle(zone)),
+ worklist_entry_(Object::Handle(zone)) {}
+
+ ~ProgramWalker() { heap_->ResetObjectIdTable(); }
+
+ // Adds the given object to the worklist if it's an object type that the
+ // visitor can visit.
+ void AddToWorklist(const Object& object) {
+ // We don't visit null, non-heap objects, or objects in the VM heap.
+ if (object.IsNull() || object.IsSmi() || object.InVMIsolateHeap()) return;
+ // Check and set visited, even if we don't end up adding this to the list.
+ if (heap_->GetObjectId(object.raw()) != 0) return;
+ heap_->SetObjectId(object.raw(), 1);
+ if (object.IsClass() ||
+ (object.IsFunction() && visitor_->IsFunctionVisitor()) ||
+ (object.IsCode() && visitor_->IsCodeVisitor())) {
+ worklist_.Add(object);
+ }
+ }
+
+ void VisitWorklist() {
+ while (!worklist_.IsEmpty()) {
+ worklist_entry_ = worklist_.Remove();
+ if (worklist_entry_.IsClass()) {
+ VisitClass(Class::Cast(worklist_entry_));
+ } else if (worklist_entry_.IsFunction()) {
+ VisitFunction(Function::Cast(worklist_entry_));
+ } else if (worklist_entry_.IsCode()) {
+ VisitCode(Code::Cast(worklist_entry_));
+ } else {
+ FATAL1("Got unexpected object %s", worklist_entry_.ToCString());
+ }
+ }
+ }
+
+ private:
void VisitClass(const Class& cls) {
- ASSERT(!cls.IsNull());
- if (cls.InVMIsolateHeap()) return;
visitor_->VisitClass(cls);
if (!visitor_->IsFunctionVisitor()) return;
- functions_ = cls.functions();
- for (intptr_t j = 0; j < functions_.Length(); j++) {
- function_ ^= functions_.At(j);
- VisitFunction(function_);
- if (function_.HasImplicitClosureFunction()) {
- function_ = function_.ImplicitClosureFunction();
- VisitFunction(function_);
+ class_functions_ = cls.functions();
+ for (intptr_t j = 0; j < class_functions_.Length(); j++) {
+ class_function_ ^= class_functions_.At(j);
+ AddToWorklist(class_function_);
+ if (class_function_.HasImplicitClosureFunction()) {
+ class_function_ = class_function_.ImplicitClosureFunction();
+ AddToWorklist(class_function_);
}
}
- functions_ = cls.invocation_dispatcher_cache();
- for (intptr_t j = 0; j < functions_.Length(); j++) {
- object_ = functions_.At(j);
- if (object_.IsFunction()) {
- function_ ^= functions_.At(j);
- VisitFunction(function_);
+ class_functions_ = cls.invocation_dispatcher_cache();
+ for (intptr_t j = 0; j < class_functions_.Length(); j++) {
+ class_object_ = class_functions_.At(j);
+ if (class_object_.IsFunction()) {
+ class_function_ ^= class_functions_.At(j);
+ AddToWorklist(class_function_);
}
}
- fields_ = cls.fields();
- for (intptr_t j = 0; j < fields_.Length(); j++) {
- field_ ^= fields_.At(j);
- if (field_.is_static() && field_.HasInitializerFunction()) {
- function_ = field_.InitializerFunction();
- VisitFunction(function_);
+ class_fields_ = cls.fields();
+ for (intptr_t j = 0; j < class_fields_.Length(); j++) {
+ class_field_ ^= class_fields_.At(j);
+ if (class_field_.is_static() && class_field_.HasInitializerFunction()) {
+ class_function_ = class_field_.InitializerFunction();
+ AddToWorklist(class_function_);
}
}
if (!visitor_->IsCodeVisitor()) return;
- code_ = cls.allocation_stub();
- if (!code_.IsNull()) VisitCode(code_);
+ class_code_ = cls.allocation_stub();
+ if (!class_code_.IsNull()) AddToWorklist(class_code_);
}
void VisitFunction(const Function& function) {
ASSERT(visitor_->IsFunctionVisitor());
- ASSERT(!function.IsNull());
- if (function.InVMIsolateHeap()) return;
visitor_->AsFunctionVisitor()->VisitFunction(function);
if (!visitor_->IsCodeVisitor() || !function.HasCode()) return;
- code_ = function.CurrentCode();
- VisitCode(code_);
+ function_code_ = function.CurrentCode();
+ AddToWorklist(function_code_);
}
void VisitCode(const Code& code) {
ASSERT(visitor_->IsCodeVisitor());
- ASSERT(!code.IsNull());
- if (code.InVMIsolateHeap()) return;
visitor_->AsCodeVisitor()->VisitCode(code);
- }
- void VisitObject(const Object& object) {
- if (object.IsNull()) return;
- if (object.IsClass()) {
- VisitClass(Class::Cast(object));
- } else if (visitor_->IsFunctionVisitor() && object.IsFunction()) {
- VisitFunction(Function::Cast(object));
- } else if (visitor_->IsCodeVisitor() && object.IsCode()) {
- VisitCode(Code::Cast(object));
+ // If the precompiler can drop function objects not needed at runtime,
+ // then some entries in the static calls table may need to be visited.
+ static_calls_array_ = code.static_calls_target_table();
+ if (static_calls_array_.IsNull()) return;
+ StaticCallsTable static_calls(static_calls_array_);
+ for (auto& view : static_calls) {
+ static_call_code_ = view.Get<Code::kSCallTableCodeTarget>();
+ AddToWorklist(static_call_code_);
}
}
- private:
+ Heap* const heap_;
ClassVisitor* const visitor_;
- Object& object_;
- Array& fields_;
- Field& field_;
- Array& functions_;
- Function& function_;
- Code& code_;
+ Worklist worklist_;
+ Object& class_object_;
+ Array& class_fields_;
+ Field& class_field_;
+ Array& class_functions_;
+ Function& class_function_;
+ Code& class_code_;
+ Code& function_code_;
+ Array& static_calls_array_;
+ Code& static_call_code_;
+ Object& worklist_entry_;
};
void ProgramVisitor::WalkProgram(Zone* zone,
Isolate* isolate,
ClassVisitor* visitor) {
auto const object_store = isolate->object_store();
- ProgramWalker walker(zone, visitor);
+ auto const heap = isolate->heap();
+ ProgramWalker walker(zone, heap, visitor);
// Walk through the libraries and patches, looking for visitable objects.
const auto& libraries =
@@ -122,29 +220,16 @@
ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
while (it.HasNext()) {
cls = it.GetNextClass();
- walker.VisitClass(cls);
+ walker.AddToWorklist(cls);
}
patches = lib.used_scripts();
for (intptr_t j = 0; j < patches.Length(); j++) {
entry = patches.At(j);
- walker.VisitObject(entry);
+ walker.AddToWorklist(entry);
}
}
- if (!visitor->IsFunctionVisitor()) return;
-
- // Function objects not necessarily reachable from classes.
- auto& function = Function::Handle(zone);
- const auto& closures =
- GrowableObjectArray::Handle(zone, object_store->closure_functions());
- ASSERT(!closures.IsNull());
- for (intptr_t i = 0; i < closures.Length(); i++) {
- function ^= closures.At(i);
- walker.VisitFunction(function);
- ASSERT(!function.HasImplicitClosureFunction());
- }
-
- // If there's a global object pool, check for functions like FfiTrampolines.
+ // If there's a global object pool, add any visitable objects.
const auto& global_object_pool =
ObjectPool::Handle(zone, object_store->global_object_pool());
if (!global_object_pool.IsNull()) {
@@ -153,25 +238,38 @@
auto const type = global_object_pool.TypeAt(i);
if (type != ObjectPool::EntryType::kTaggedObject) continue;
object = global_object_pool.ObjectAt(i);
- if (object.IsFunction() && visitor->IsFunctionVisitor()) {
- walker.VisitFunction(Function::Cast(object));
+ walker.AddToWorklist(object);
+ }
+ }
+
+ if (visitor->IsFunctionVisitor()) {
+ // Function objects not necessarily reachable from classes.
+ auto& function = Function::Handle(zone);
+ const auto& closures =
+ GrowableObjectArray::Handle(zone, object_store->closure_functions());
+ ASSERT(!closures.IsNull());
+ for (intptr_t i = 0; i < closures.Length(); i++) {
+ function ^= closures.At(i);
+ walker.AddToWorklist(function);
+ ASSERT(!function.HasImplicitClosureFunction());
+ }
+ }
+
+ if (visitor->IsCodeVisitor()) {
+ // Code objects not necessarily reachable from functions.
+ auto& code = Code::Handle(zone);
+ const auto& dispatch_table_entries =
+ Array::Handle(zone, object_store->dispatch_table_code_entries());
+ if (!dispatch_table_entries.IsNull()) {
+ for (intptr_t i = 0; i < dispatch_table_entries.Length(); i++) {
+ code ^= dispatch_table_entries.At(i);
+ walker.AddToWorklist(code);
}
}
}
- if (!visitor->IsCodeVisitor()) return;
-
- // Code objects not necessarily reachable from functions.
- auto& code = Code::Handle(zone);
- const auto& dispatch_table_entries =
- Array::Handle(zone, object_store->dispatch_table_code_entries());
- if (!dispatch_table_entries.IsNull()) {
- for (intptr_t i = 0; i < dispatch_table_entries.Length(); i++) {
- code ^= dispatch_table_entries.At(i);
- if (code.IsNull()) continue;
- walker.VisitCode(code);
- }
- }
+ // Walk the program starting from any roots we added to the worklist.
+ walker.VisitWorklist();
}
#if !defined(DART_PRECOMPILED_RUNTIME)
@@ -710,6 +808,7 @@
Smi& reason_and_flags_;
};
+ if (FLAG_precompiled_mode) return;
DedupDeoptEntriesVisitor visitor(zone);
WalkProgram(zone, isolate, &visitor);
}
@@ -1072,9 +1171,13 @@
void VisitCode(const Code& code) {
instructions_ = code.instructions();
instructions_ = Dedup(instructions_);
+ code.set_instructions(instructions_);
+ if (code.IsDisabled()) {
+ instructions_ = code.active_instructions();
+ instructions_ = Dedup(instructions_);
+ }
code.SetActiveInstructions(instructions_,
code.UncheckedEntryPointOffset());
- code.set_instructions(instructions_);
if (!code.IsFunctionCode()) return;
function_ = code.function();
if (function_.IsNull()) return;
@@ -1147,7 +1250,7 @@
ShareMegamorphicBuckets(zone, isolate);
NormalizeAndDedupCompressedStackMaps(zone, isolate);
DedupPcDescriptors(zone, isolate);
- NOT_IN_PRECOMPILED(DedupDeoptEntries(zone, isolate));
+ DedupDeoptEntries(zone, isolate);
#if defined(DART_PRECOMPILER)
DedupCatchEntryMovesMaps(zone, isolate);
DedupUnlinkedCalls(zone, isolate);
diff --git a/runtime/vm/program_visitor.h b/runtime/vm/program_visitor.h
index 9edc6ad..e10ffd2 100644
--- a/runtime/vm/program_visitor.h
+++ b/runtime/vm/program_visitor.h
@@ -28,11 +28,11 @@
// VisitFunction.
//
// There are no guarantees for the order in which objects of a given type will
-// be visited or how many times they will be visited. The only guarantee is that
-// objects will be visited before any visitable sub-objects they contain. For
-// example, if a FunctionVisitor has a VisitClass implementation that drops
-// methods from a class, the function objects for those methods will not be
-// visited unless they are also found via another source of function objects.
+// be visited, but each object will be visited only once. In addition, each
+// object is visited before any visitable sub-objects it contains. For example,
+// this means a FunctionVisitor with a VisitClass implementation that drops
+// methods from a class will not visit the dropped methods unless they are also
+// found via another source of function objects.
//
// Note that WalkProgram only visits objects in the isolate heap. Deduplicating
// visitors that want to use VM objects as canonical when possible should
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 85c05f2..871aa0b1 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -64,11 +64,11 @@
}
}
const intptr_t class_id = ClassIdTag::decode(tags);
- if (!isolate_group->class_table()->IsValidIndex(class_id)) {
+ if (!isolate_group->shared_class_table()->IsValidIndex(class_id)) {
FATAL1("Invalid class id encountered %" Pd "\n", class_id);
}
if (class_id == kNullCid &&
- isolate_group->class_table()->HasValidClassAt(class_id)) {
+ isolate_group->shared_class_table()->HasValidClassAt(class_id)) {
// Null class not yet initialized; skip.
return;
}
@@ -241,7 +241,7 @@
const bool use_saved_class_table = false;
#endif
- auto class_table = isolate_group->class_table();
+ auto class_table = isolate_group->shared_class_table();
ASSERT(use_saved_class_table || class_table->SizeAt(class_id) > 0);
if (!class_table->IsValidIndex(class_id) ||
(!class_table->HasValidClassAt(class_id) && !use_saved_class_table)) {
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 07293c9..b3758a2 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -2665,6 +2665,7 @@
friend class LinkedHashMapSerializationCluster;
friend class LinkedHashMapDeserializationCluster;
+ friend class CodeSerializationCluster;
friend class CodeDeserializationCluster;
friend class Deserializer;
friend class RawCode;
diff --git a/runtime/vm/regexp.cc b/runtime/vm/regexp.cc
index 95567a9..08384be 100644
--- a/runtime/vm/regexp.cc
+++ b/runtime/vm/regexp.cc
@@ -14,12 +14,15 @@
#include "vm/dart_entry.h"
#include "vm/regexp_assembler.h"
#include "vm/regexp_assembler_bytecode.h"
-#include "vm/regexp_assembler_ir.h"
#include "vm/regexp_ast.h"
#include "vm/symbols.h"
#include "vm/thread.h"
#include "vm/unibrow-inl.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/regexp_assembler_ir.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
#define Z (zone())
namespace dart {
@@ -701,7 +704,7 @@
// Omit flushing the trace. We discard the entire stack frame anyway.
- if (!label()->IsBound()) {
+ if (!label()->is_bound()) {
// We are completely independent of the trace, since we ignore it,
// so this code can be used as the generic version.
assembler->BindBlock(label());
@@ -728,7 +731,7 @@
return;
}
RegExpMacroAssembler* assembler = compiler->macro_assembler();
- if (!label()->IsBound()) {
+ if (!label()->is_bound()) {
assembler->BindBlock(label());
}
switch (action_) {
@@ -1126,7 +1129,7 @@
EmitDoubleBoundaryTest(masm, ranges->At(cut_index),
ranges->At(cut_index + 1) - 1, &dummy, in_range_label,
&dummy);
- ASSERT(!dummy.IsLinked());
+ ASSERT(!dummy.is_linked());
// Cut out the single range by rewriting the array. This creates a new
// range that is a merger of the two ranges on either side of the one we
// are cutting out. The oddity of the labels is preserved.
@@ -1314,7 +1317,7 @@
GenerateBranches(masm, ranges, start_index, new_end_index, min_char,
border - 1, &dummy, even_label, odd_label);
- if (handle_rest.IsLinked()) {
+ if (handle_rest.is_linked()) {
masm->BindBlock(&handle_rest);
bool flip = (new_start_index & 1) != (start_index & 1);
GenerateBranches(masm, ranges, new_start_index, end_index, border, max_char,
@@ -1432,7 +1435,7 @@
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
if (trace->is_trivial()) {
- if (label_.IsBound()) {
+ if (label_.is_bound()) {
// We are being asked to generate a generic version, but that's already
// been done so just go to it.
macro_assembler->GoTo(&label_);
@@ -3352,7 +3355,7 @@
AlternativeGeneration* alt_gen,
intptr_t preload_characters,
bool next_expects_preload) {
- if (!alt_gen->possible_success.IsLinked()) return;
+ if (!alt_gen->possible_success.is_linked()) return;
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
macro_assembler->BindBlock(&alt_gen->possible_success);
@@ -3621,7 +3624,7 @@
printer.PrintBit("WI", info->follows_word_interest);
printer.PrintBit("SI", info->follows_start_interest);
BlockLabel* label = that->label();
- if (label->IsBound()) printer.PrintPositive("@", label->Position());
+ if (label->is_bound()) printer.PrintPositive("@", label->pos());
OS::PrintErr(
"}\"];\n"
" a%p -> n%p [style=dashed, color=grey, arrowhead=none];\n",
diff --git a/runtime/vm/regexp.h b/runtime/vm/regexp.h
index 170d412..7cefc38 100644
--- a/runtime/vm/regexp.h
+++ b/runtime/vm/regexp.h
@@ -7,9 +7,6 @@
#include "platform/unicode.h"
-#include "vm/compiler/assembler/assembler.h"
-#include "vm/compiler/backend/flow_graph_compiler.h"
-#include "vm/compiler/backend/il.h"
#include "vm/object.h"
#include "vm/regexp_assembler.h"
#include "vm/splay-tree.h"
diff --git a/runtime/vm/regexp_assembler.cc b/runtime/vm/regexp_assembler.cc
index ab242c4..8fa69ac 100644
--- a/runtime/vm/regexp_assembler.cc
+++ b/runtime/vm/regexp_assembler.cc
@@ -10,6 +10,7 @@
#include "vm/flags.h"
#include "vm/regexp.h"
+#include "vm/runtime_entry.h"
#include "vm/unibrow-inl.h"
namespace dart {
@@ -96,8 +97,7 @@
false /* is_float */,
reinterpret_cast<RuntimeFunction>(&CaseInsensitiveCompareUTF16));
-BlockLabel::BlockLabel()
- : block_(NULL), is_bound_(false), is_linked_(false), pos_(-1) {
+BlockLabel::BlockLabel() {
#if !defined(DART_PRECOMPILED_RUNTIME)
if (!FLAG_interpret_irregexp) {
// Only needed by the compiled IR backend.
diff --git a/runtime/vm/regexp_assembler.h b/runtime/vm/regexp_assembler.h
index 818f573..d7a460b 100644
--- a/runtime/vm/regexp_assembler.h
+++ b/runtime/vm/regexp_assembler.h
@@ -5,9 +5,12 @@
#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_H_
#define RUNTIME_VM_REGEXP_ASSEMBLER_H_
+#include "vm/object.h"
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/il.h"
-#include "vm/object.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
@@ -32,60 +35,59 @@
// Used by the IR assembler.
public:
BlockLabel();
-
- JoinEntryInstr* block() const { return block_; }
-
- bool IsBound() const { return is_bound_; }
- void SetBound(intptr_t block_id) {
- ASSERT(!is_bound_);
- block_->set_block_id(block_id);
- is_bound_ = true;
- }
-
- bool IsLinked() const { return !is_bound_ && is_linked_; }
- void SetLinked() { is_linked_ = true; }
-
- intptr_t Position() const {
- ASSERT(IsBound());
- return block_->block_id();
- }
-
- private:
- JoinEntryInstr* block_;
-
- bool is_bound_;
- bool is_linked_;
-
- // Used by the bytecode assembler.
- public:
~BlockLabel() { ASSERT(!is_linked()); }
intptr_t pos() const { return pos_; }
- bool is_bound() const { return IsBound(); }
- bool is_linked() const { return IsLinked(); }
+ bool is_bound() const { return is_bound_; }
+ bool is_linked() const { return !is_bound_ && is_linked_; }
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ JoinEntryInstr* block() const { return block_; }
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
void Unuse() {
- pos_ = 0;
+ pos_ = -1;
is_bound_ = false;
is_linked_ = false;
}
- void bind_to(intptr_t pos) {
+ void BindTo(intptr_t pos) {
pos_ = pos;
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ if (block_ != nullptr) block_->set_block_id(pos);
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
is_bound_ = true;
is_linked_ = false;
ASSERT(is_bound());
}
- void link_to(intptr_t pos) {
+ // Used by bytecode assembler to form a linked list out of
+ // forward jumps to an unbound label.
+ void LinkTo(intptr_t pos) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ ASSERT(block_ == nullptr);
+#endif
+ ASSERT(!is_bound_);
pos_ = pos;
- is_bound_ = false;
is_linked_ = true;
- ASSERT(is_linked());
+ }
+
+ // Used by IR builder to mark block label as used.
+ void SetLinked() {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ ASSERT(block_ != nullptr);
+#endif
+ if (!is_bound_) {
+ is_linked_ = true;
+ }
}
private:
- intptr_t pos_;
+ bool is_bound_ = false;
+ bool is_linked_ = false;
+ intptr_t pos_ = -1;
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ JoinEntryInstr* block_ = nullptr;
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
};
class RegExpMacroAssembler : public ZoneAllocated {
diff --git a/runtime/vm/regexp_assembler_bytecode.cc b/runtime/vm/regexp_assembler_bytecode.cc
index c075a86..4118969 100644
--- a/runtime/vm/regexp_assembler_bytecode.cc
+++ b/runtime/vm/regexp_assembler_bytecode.cc
@@ -44,7 +44,7 @@
*reinterpret_cast<uint32_t*>(buffer_->data() + fixup) = pc_;
}
}
- l->bind_to(pc_);
+ l->BindTo(pc_);
}
void BytecodeRegExpMacroAssembler::EmitOrLink(BlockLabel* l) {
@@ -56,7 +56,7 @@
if (l->is_linked()) {
pos = l->pos();
}
- l->link_to(pc_);
+ l->LinkTo(pc_);
Emit32(pos);
}
}
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp_assembler_ir.cc
index 500a52f..2c295ef 100644
--- a/runtime/vm/regexp_assembler_ir.cc
+++ b/runtime/vm/regexp_assembler_ir.cc
@@ -682,10 +682,10 @@
// If the BlockLabel does not yet contain a block, it is created.
// If there is a current instruction, append a goto to the bound block.
void IRRegExpMacroAssembler::BindBlock(BlockLabel* label) {
- ASSERT(!label->IsBound());
+ ASSERT(!label->is_bound());
ASSERT(label->block()->next() == NULL);
- label->SetBound(block_id_.Alloc());
+ label->BindTo(block_id_.Alloc());
blocks_.Add(label->block());
if (current_instruction_ != NULL) {
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 6bb8000..32bab52 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -4,14 +4,14 @@
#include "vm/runtime_entry.h"
+#include "vm/code_descriptors.h"
#include "vm/code_patcher.h"
-#include "vm/compiler/assembler/assembler.h"
-#include "vm/compiler/frontend/bytecode_reader.h"
+#include "vm/compiler/api/deopt_id.h"
+#include "vm/compiler/api/type_check_mode.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/debugger.h"
-#include "vm/deopt_instructions.h"
#include "vm/exceptions.h"
#include "vm/flags.h"
#include "vm/heap/verifier.h"
@@ -30,6 +30,10 @@
#include "vm/thread_registry.h"
#include "vm/type_testing_stubs.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/deopt_instructions.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
DEFINE_FLAG(
@@ -342,14 +346,12 @@
const intptr_t length =
Array::LengthOf(reinterpret_cast<RawArray*>(object));
add_to_remembered_set =
- CreateArrayInstr::WillAllocateNewOrRemembered(length);
+ compiler::target::WillAllocateNewOrRememberedArray(length);
} else if (object->IsContext()) {
const intptr_t num_context_variables =
Context::NumVariables(reinterpret_cast<RawContext*>(object));
add_to_remembered_set =
- AllocateContextInstr::WillAllocateNewOrRemembered(
- num_context_variables) ||
- AllocateUninitializedContextInstr::WillAllocateNewOrRemembered(
+ compiler::target::WillAllocateNewOrRememberedContext(
num_context_variables);
}
@@ -416,10 +418,14 @@
// Code inlined in the caller should have optimized the case where the
// instantiator can be reused as type argument vector.
ASSERT(!type_arguments.IsUninstantiatedIdentity());
- type_arguments = type_arguments.InstantiateAndCanonicalizeFrom(
- instantiator_type_arguments, function_type_arguments);
- ASSERT(type_arguments.IsNull() || type_arguments.IsInstantiated());
- arguments.SetReturn(type_arguments);
+ thread->isolate_group()->RunWithStoppedMutators(
+ [&]() {
+ type_arguments = type_arguments.InstantiateAndCanonicalizeFrom(
+ instantiator_type_arguments, function_type_arguments);
+ ASSERT(type_arguments.IsNull() || type_arguments.IsInstantiated());
+ arguments.SetReturn(type_arguments);
+ },
+ /*use_force_growth=*/true);
}
// Instantiate type.
@@ -596,6 +602,7 @@
// This operation is currently very slow (lookup of code is not efficient yet).
static void UpdateTypeTestCache(
Zone* zone,
+ Thread* thread,
const Instance& instance,
const AbstractType& type,
const TypeArguments& instantiator_type_arguments,
@@ -639,94 +646,103 @@
instance_type_arguments = instance.GetTypeArguments();
}
}
- const intptr_t len = new_cache.NumberOfChecks();
- if (len >= FLAG_max_subtype_cache_entries) {
- if (FLAG_trace_type_checks) {
- OS::PrintErr("Not updating subtype test cache as its length reached %d\n",
- FLAG_max_subtype_cache_entries);
- }
- return;
- }
+ thread->isolate_group()->RunWithStoppedMutators(
+ [&]() {
+ const intptr_t len = new_cache.NumberOfChecks();
+ if (len >= FLAG_max_subtype_cache_entries) {
+ if (FLAG_trace_type_checks) {
+ OS::PrintErr(
+ "Not updating subtype test cache as its length reached %d\n",
+ FLAG_max_subtype_cache_entries);
+ }
+ return;
+ }
#if defined(DEBUG)
- ASSERT(instance_type_arguments.IsNull() ||
- instance_type_arguments.IsCanonical());
- ASSERT(instantiator_type_arguments.IsNull() ||
- instantiator_type_arguments.IsCanonical());
- ASSERT(function_type_arguments.IsNull() ||
- function_type_arguments.IsCanonical());
- ASSERT(instance_parent_function_type_arguments.IsNull() ||
- instance_parent_function_type_arguments.IsCanonical());
- ASSERT(instance_delayed_type_arguments.IsNull() ||
- instance_delayed_type_arguments.IsCanonical());
- auto& last_instance_class_id_or_function = Object::Handle(zone);
- auto& last_instance_type_arguments = TypeArguments::Handle(zone);
- auto& last_instantiator_type_arguments = TypeArguments::Handle(zone);
- auto& last_function_type_arguments = TypeArguments::Handle(zone);
- auto& last_instance_parent_function_type_arguments =
- TypeArguments::Handle(zone);
- auto& last_instance_delayed_type_arguments = TypeArguments::Handle(zone);
- Bool& last_result = Bool::Handle(zone);
- for (intptr_t i = 0; i < len; ++i) {
- new_cache.GetCheck(
- i, &last_instance_class_id_or_function, &last_instance_type_arguments,
- &last_instantiator_type_arguments, &last_function_type_arguments,
- &last_instance_parent_function_type_arguments,
- &last_instance_delayed_type_arguments, &last_result);
- if ((last_instance_class_id_or_function.raw() ==
- instance_class_id_or_function.raw()) &&
- (last_instance_type_arguments.raw() == instance_type_arguments.raw()) &&
- (last_instantiator_type_arguments.raw() ==
- instantiator_type_arguments.raw()) &&
- (last_function_type_arguments.raw() == function_type_arguments.raw()) &&
- (last_instance_parent_function_type_arguments.raw() ==
- instance_parent_function_type_arguments.raw()) &&
- (last_instance_delayed_type_arguments.raw() ==
- instance_delayed_type_arguments.raw())) {
- OS::PrintErr(" Error in test cache %p ix: %" Pd ",", new_cache.raw(), i);
- PrintTypeCheck(" duplicate cache entry", instance, type,
- instantiator_type_arguments, function_type_arguments,
- result);
- UNREACHABLE();
- return;
- }
- }
+ ASSERT(instance_type_arguments.IsNull() ||
+ instance_type_arguments.IsCanonical());
+ ASSERT(instantiator_type_arguments.IsNull() ||
+ instantiator_type_arguments.IsCanonical());
+ ASSERT(function_type_arguments.IsNull() ||
+ function_type_arguments.IsCanonical());
+ ASSERT(instance_parent_function_type_arguments.IsNull() ||
+ instance_parent_function_type_arguments.IsCanonical());
+ ASSERT(instance_delayed_type_arguments.IsNull() ||
+ instance_delayed_type_arguments.IsCanonical());
+ auto& last_instance_class_id_or_function = Object::Handle(zone);
+ auto& last_instance_type_arguments = TypeArguments::Handle(zone);
+ auto& last_instantiator_type_arguments = TypeArguments::Handle(zone);
+ auto& last_function_type_arguments = TypeArguments::Handle(zone);
+ auto& last_instance_parent_function_type_arguments =
+ TypeArguments::Handle(zone);
+ auto& last_instance_delayed_type_arguments =
+ TypeArguments::Handle(zone);
+ Bool& last_result = Bool::Handle(zone);
+ for (intptr_t i = 0; i < len; ++i) {
+ new_cache.GetCheck(
+ i, &last_instance_class_id_or_function,
+ &last_instance_type_arguments, &last_instantiator_type_arguments,
+ &last_function_type_arguments,
+ &last_instance_parent_function_type_arguments,
+ &last_instance_delayed_type_arguments, &last_result);
+ if ((last_instance_class_id_or_function.raw() ==
+ instance_class_id_or_function.raw()) &&
+ (last_instance_type_arguments.raw() ==
+ instance_type_arguments.raw()) &&
+ (last_instantiator_type_arguments.raw() ==
+ instantiator_type_arguments.raw()) &&
+ (last_function_type_arguments.raw() ==
+ function_type_arguments.raw()) &&
+ (last_instance_parent_function_type_arguments.raw() ==
+ instance_parent_function_type_arguments.raw()) &&
+ (last_instance_delayed_type_arguments.raw() ==
+ instance_delayed_type_arguments.raw())) {
+ // Some other isolate might have updated the cache between entry was
+ // found missing and now.
+ return;
+ }
+ }
#endif
- new_cache.AddCheck(instance_class_id_or_function, instance_type_arguments,
- instantiator_type_arguments, function_type_arguments,
- instance_parent_function_type_arguments,
- instance_delayed_type_arguments, result);
- if (FLAG_trace_type_checks) {
- AbstractType& test_type = AbstractType::Handle(zone, type.raw());
- if (!test_type.IsInstantiated()) {
- test_type = type.InstantiateFrom(instantiator_type_arguments,
- function_type_arguments, kAllFree, NULL,
- Heap::kNew);
- }
- const auto& type_class = Class::Handle(zone, test_type.type_class());
- const auto& instance_class_name =
- String::Handle(zone, instance_class.Name());
- OS::PrintErr(
- " Updated test cache %p ix: %" Pd
- " with "
- "(cid-or-fun: %p, type-args: %p, i-type-args: %p, f-type-args: %p, "
- "p-type-args: %p, d-type-args: %p, result: %s)\n"
- " instance [class: (%p '%s' cid: %" Pd
- "), type-args: %p %s]\n"
- " test-type [class: (%p '%s' cid: %" Pd
- "), i-type-args: %p %s, f-type-args: %p %s]\n",
- new_cache.raw(), len, instance_class_id_or_function.raw(),
- instance_type_arguments.raw(), instantiator_type_arguments.raw(),
- function_type_arguments.raw(),
- instance_parent_function_type_arguments.raw(),
- instance_delayed_type_arguments.raw(), result.ToCString(),
- instance_class.raw(), instance_class_name.ToCString(),
- instance_class.id(), instance_type_arguments.raw(),
- instance_type_arguments.ToCString(), type_class.raw(),
- String::Handle(zone, type_class.Name()).ToCString(), type_class.id(),
- instantiator_type_arguments.raw(),
- instantiator_type_arguments.ToCString(), function_type_arguments.raw(),
- function_type_arguments.ToCString());
- }
+ new_cache.AddCheck(instance_class_id_or_function,
+ instance_type_arguments, instantiator_type_arguments,
+ function_type_arguments,
+ instance_parent_function_type_arguments,
+ instance_delayed_type_arguments, result);
+ if (FLAG_trace_type_checks) {
+ AbstractType& test_type = AbstractType::Handle(zone, type.raw());
+ if (!test_type.IsInstantiated()) {
+ test_type = type.InstantiateFrom(instantiator_type_arguments,
+ function_type_arguments, kAllFree,
+ NULL, Heap::kNew);
+ }
+ const auto& type_class = Class::Handle(zone, test_type.type_class());
+ const auto& instance_class_name =
+ String::Handle(zone, instance_class.Name());
+ OS::PrintErr(
+ " Updated test cache %p ix: %" Pd
+ " with "
+ "(cid-or-fun: %p, type-args: %p, i-type-args: %p, f-type-args: "
+ "%p, "
+ "p-type-args: %p, d-type-args: %p, result: %s)\n"
+ " instance [class: (%p '%s' cid: %" Pd
+ "), type-args: %p %s]\n"
+ " test-type [class: (%p '%s' cid: %" Pd
+ "), i-type-args: %p %s, f-type-args: %p %s]\n",
+ new_cache.raw(), len, instance_class_id_or_function.raw(),
+ instance_type_arguments.raw(), instantiator_type_arguments.raw(),
+ function_type_arguments.raw(),
+ instance_parent_function_type_arguments.raw(),
+ instance_delayed_type_arguments.raw(), result.ToCString(),
+ instance_class.raw(), instance_class_name.ToCString(),
+ instance_class.id(), instance_type_arguments.raw(),
+ instance_type_arguments.ToCString(), type_class.raw(),
+ String::Handle(zone, type_class.Name()).ToCString(),
+ type_class.id(), instantiator_type_arguments.raw(),
+ instantiator_type_arguments.ToCString(),
+ function_type_arguments.raw(),
+ function_type_arguments.ToCString());
+ }
+ },
+ /*use_force_growth=*/true);
}
// Check that the given instance is an instance of the given type.
@@ -756,7 +772,7 @@
PrintTypeCheck("InstanceOf", instance, type, instantiator_type_arguments,
function_type_arguments, result);
}
- UpdateTypeTestCache(zone, instance, type, instantiator_type_arguments,
+ UpdateTypeTestCache(zone, thread, instance, type, instantiator_type_arguments,
function_type_arguments, result, cache);
arguments.SetReturn(result);
}
@@ -920,18 +936,26 @@
TypeTestingStubCallPattern tts_pattern(caller_frame->pc());
const intptr_t stc_pool_idx = tts_pattern.GetSubtypeTestCachePoolIndex();
- // The pool entry must be initialized to `null` when we patch it.
- ASSERT(pool.ObjectAt(stc_pool_idx) == Object::null());
- cache = SubtypeTestCache::New();
- pool.SetObjectAt(stc_pool_idx, cache);
+ thread->isolate_group()->RunWithStoppedMutators(
+ [&]() {
+ // If nobody has updated the pool since the check, we are
+ // updating it now.
+ if (pool.ObjectAt(stc_pool_idx) == Object::null()) {
+ cache = SubtypeTestCache::New();
+ pool.SetObjectAt(stc_pool_idx, cache);
+ }
+ },
+ /*use_force_growth=*/true);
#else
UNREACHABLE();
#endif
}
- UpdateTypeTestCache(zone, src_instance, dst_type,
- instantiator_type_arguments, function_type_arguments,
- Bool::True(), cache);
+ if (!cache.IsNull()) { // we might have lost the race to set up new cache
+ UpdateTypeTestCache(zone, thread, src_instance, dst_type,
+ instantiator_type_arguments, function_type_arguments,
+ Bool::True(), cache);
+ }
}
arguments.SetReturn(src_instance);
@@ -1458,36 +1482,45 @@
Isolate* isolate,
uword frame_pc,
const UnlinkedCall& unlinked_call) {
- auto object_store = isolate->object_store();
- if (object_store->saved_unlinked_calls() == Array::null()) {
+ IsolateGroup* isolate_group = isolate->group();
+ if (isolate_group->saved_unlinked_calls() == Array::null()) {
const auto& initial_map =
Array::Handle(zone, HashTables::New<UnlinkedCallMap>(16, Heap::kOld));
- object_store->set_saved_unlinked_calls(initial_map);
+ isolate_group->set_saved_unlinked_calls(initial_map);
}
- UnlinkedCallMap unlinked_call_map(zone, object_store->saved_unlinked_calls());
+ UnlinkedCallMap unlinked_call_map(zone,
+ isolate_group->saved_unlinked_calls());
const auto& pc = Integer::Handle(Integer::NewFromUint64(frame_pc));
- const bool was_present = unlinked_call_map.UpdateOrInsert(pc, unlinked_call);
- // We transition at most once out of UnlinkedCall state.
- RELEASE_ASSERT(!was_present);
- object_store->set_saved_unlinked_calls(unlinked_call_map.Release());
+ // Some other isolate might have updated unlinked_call_map[pc] too, but
+ // their update should be identical to ours.
+ UnlinkedCall& new_or_old_value = UnlinkedCall::Handle(
+ zone, UnlinkedCall::RawCast(
+ unlinked_call_map.InsertOrGetValue(pc, unlinked_call)));
+ RELEASE_ASSERT(new_or_old_value.raw() == unlinked_call.raw());
+ isolate_group->set_saved_unlinked_calls(unlinked_call_map.Release());
}
#if defined(DART_PRECOMPILED_RUNTIME)
static RawUnlinkedCall* LoadUnlinkedCall(Zone* zone,
Isolate* isolate,
- uword pc) {
- auto object_store = isolate->object_store();
- ASSERT(object_store->saved_unlinked_calls() != Array::null());
+ uword pc,
+ bool is_monomorphic_hit) {
+ IsolateGroup* isolate_group = isolate->group();
+ ASSERT(isolate_group->saved_unlinked_calls() != Array::null());
- UnlinkedCallMap unlinked_call_map(zone, object_store->saved_unlinked_calls());
+ UnlinkedCallMap unlinked_call_map(zone,
+ isolate_group->saved_unlinked_calls());
+
const auto& pc_integer = Integer::Handle(Integer::NewFromUint64(pc));
const auto& unlinked_call = UnlinkedCall::Cast(
Object::Handle(zone, unlinked_call_map.GetOrDie(pc_integer)));
- // Since we transition out of the monomorphic state only once, we should not
- // need the saved unlinked call anymore.
- unlinked_call_map.Remove(pc_integer);
- object_store->set_saved_unlinked_calls(unlinked_call_map.Release());
+ // Only remove entry from unlinked_call_map if we are actually transitioning
+ // out of monomorphic state.
+ if (!is_monomorphic_hit) {
+ unlinked_call_map.Remove(pc_integer);
+ isolate_group->set_saved_unlinked_calls(unlinked_call_map.Release());
+ }
return unlinked_call.raw();
}
@@ -1624,8 +1657,8 @@
code = StubCode::MonomorphicSmiableCheck().raw();
}
}
- CodePatcher::PatchSwitchableCallAt(caller_frame_->pc(), caller_code_, object,
- code);
+ CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ thread_, caller_frame_->pc(), caller_code_, object, code);
// Return the ICData. The miss stub will jump to continue in the IC lookup
// stub.
@@ -1692,11 +1725,16 @@
UNREACHABLE();
}
+ // The site might have just been updated to monomorphic state with same
+ // exact class id, in which case we are staying in monomorphic state.
+ bool is_monomorphic_hit = old_expected_cid == receiver_.GetClassId();
+
String& name = String::Handle(zone_);
Array& descriptor = Array::Handle(zone_);
if (FLAG_use_bare_instructions && FLAG_dedup_instructions) {
const UnlinkedCall& unlinked_call = UnlinkedCall::Handle(
- zone_, LoadUnlinkedCall(zone_, isolate_, caller_frame_->pc()));
+ zone_, LoadUnlinkedCall(zone_, isolate_, caller_frame_->pc(),
+ is_monomorphic_hit));
name = unlinked_call.target_name();
descriptor = unlinked_call.args_descriptor();
@@ -1741,6 +1779,14 @@
ic_data.AddReceiverCheck(old_expected_cid, old_target);
}
+ if (is_monomorphic_hit) {
+ // The site just have been updated to monomorphic state with same
+ // exact class id - do nothing in that case: stub will call through ic data.
+ arguments_.SetArgAt(0, StubCode::ICCallThroughCode());
+ arguments_.SetReturn(ic_data);
+ return;
+ }
+
const Function& target_function = Function::Handle(
zone_, ResolveAndAddReceiverCheck(name, descriptor, ic_data));
@@ -1756,8 +1802,8 @@
cache.set_lower_limit(lower);
cache.set_upper_limit(upper);
const Code& stub = StubCode::SingleTargetCall();
- CodePatcher::PatchSwitchableCallAt(caller_frame_->pc(), caller_code_, cache,
- stub);
+ CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ thread_, caller_frame_->pc(), caller_code_, cache, stub);
// Return the ICData. The miss stub will jump to continue in the IC call
// stub.
arguments_.SetArgAt(0, StubCode::ICCallThroughCode());
@@ -1767,8 +1813,8 @@
// Patch to call through stub.
const Code& stub = StubCode::ICCallThroughCode();
- CodePatcher::PatchSwitchableCallAt(caller_frame_->pc(), caller_code_, ic_data,
- stub);
+ CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ thread_, caller_frame_->pc(), caller_code_, ic_data, stub);
// Return the ICData. The miss stub will jump to continue in the IC lookup
// stub.
@@ -1784,8 +1830,8 @@
const Code& stub = ic_data.is_tracking_exactness()
? StubCode::OneArgCheckInlineCacheWithExactnessCheck()
: StubCode::OneArgCheckInlineCache();
- CodePatcher::PatchInstanceCallAt(caller_frame_->pc(), caller_code_, ic_data,
- stub);
+ CodePatcher::PatchInstanceCallAtWithMutatorsStopped(
+ thread_, caller_frame_->pc(), caller_code_, ic_data, stub);
if (FLAG_trace_ic) {
OS::PrintErr("Instance call at %" Px
" switching to polymorphic dispatch, %s\n",
@@ -1844,8 +1890,8 @@
// Call site is not single target, switch to call using ICData.
const Code& stub = StubCode::ICCallThroughCode();
- CodePatcher::PatchSwitchableCallAt(caller_frame_->pc(), caller_code_, ic_data,
- stub);
+ CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ thread_, caller_frame_->pc(), caller_code_, ic_data, stub);
// Return the ICData. The single target stub will jump to continue in the
// IC call stub.
@@ -1892,20 +1938,25 @@
Code::Handle(zone_, target_function.EnsureHasCode());
const Smi& expected_cid =
Smi::Handle(zone_, Smi::New(receiver_.GetClassId()));
- CodePatcher::PatchSwitchableCallAt(caller_frame_->pc(), caller_code_,
- expected_cid, target_code);
+ CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ thread_, caller_frame_->pc(), caller_code_, expected_cid, target_code);
arguments_.SetArgAt(0, target_code);
arguments_.SetReturn(expected_cid);
} else {
- ic_data.AddReceiverCheck(receiver_.GetClassId(), target_function);
+ // IC entry might have been added while we waited to get into runtime.
+ GrowableArray<intptr_t> class_ids(1);
+ class_ids.Add(receiver_.GetClassId());
+ if (ic_data.FindCheck(class_ids) == -1) {
+ ic_data.AddReceiverCheck(receiver_.GetClassId(), target_function);
+ }
if (number_of_checks > FLAG_max_polymorphic_checks) {
// Switch to megamorphic call.
const MegamorphicCache& cache = MegamorphicCache::Handle(
zone_, MegamorphicCacheTable::Lookup(thread_, name, descriptor));
const Code& stub = StubCode::MegamorphicCall();
- CodePatcher::PatchSwitchableCallAt(caller_frame_->pc(), caller_code_,
- cache, stub);
+ CodePatcher::PatchSwitchableCallAtWithMutatorsStopped(
+ thread_, caller_frame_->pc(), caller_code_, cache, stub);
arguments_.SetArgAt(0, stub);
arguments_.SetReturn(cache);
} else {
@@ -1946,14 +1997,14 @@
}
void SwitchableCallHandler::HandleMiss(const Object& old_data,
- const Code& old_target) {
+ const Code& old_code) {
switch (old_data.GetClassId()) {
case kUnlinkedCallCid:
- ASSERT(old_target.raw() == StubCode::SwitchableCallMiss().raw());
+ ASSERT(old_code.raw() == StubCode::SwitchableCallMiss().raw());
DoUnlinkedCall(UnlinkedCall::Cast(old_data));
break;
case kMonomorphicSmiableCallCid:
- ASSERT(old_target.raw() == StubCode::MonomorphicSmiableCheck().raw());
+ ASSERT(old_code.raw() == StubCode::MonomorphicSmiableCheck().raw());
FALL_THROUGH;
#if defined(DART_PRECOMPILED_RUNTIME)
case kSmiCid:
@@ -1965,15 +2016,15 @@
DoMonomorphicMiss(old_data);
break;
case kSingleTargetCacheCid:
- ASSERT(old_target.raw() == StubCode::SingleTargetCall().raw());
+ ASSERT(old_code.raw() == StubCode::SingleTargetCall().raw());
DoSingleTargetMiss(SingleTargetCache::Cast(old_data));
break;
case kICDataCid:
- ASSERT(old_target.raw() == StubCode::ICCallThroughCode().raw());
+ ASSERT(old_code.raw() == StubCode::ICCallThroughCode().raw());
DoICDataMiss(ICData::Cast(old_data));
break;
case kMegamorphicCacheCid:
- ASSERT(old_target.raw() == StubCode::MegamorphicCall().raw());
+ ASSERT(old_code.raw() == StubCode::MegamorphicCall().raw());
DoMegamorphicMiss(MegamorphicCache::Cast(old_data));
break;
default:
@@ -2006,20 +2057,24 @@
Object& old_data = Object::Handle(zone);
Code& old_code = Code::Handle(zone);
+ thread->isolate_group()->RunWithStoppedMutators(
+ [&]() {
#if defined(DART_PRECOMPILED_RUNTIME)
- old_data =
- CodePatcher::GetSwitchableCallDataAt(caller_frame->pc(), caller_code);
+ old_data = CodePatcher::GetSwitchableCallDataAt(caller_frame->pc(),
+ caller_code);
#if defined(DEBUG)
- old_code ^=
- CodePatcher::GetSwitchableCallTargetAt(caller_frame->pc(), caller_code);
+ old_code ^= CodePatcher::GetSwitchableCallTargetAt(caller_frame->pc(),
+ caller_code);
#endif
#else
- old_code ^= CodePatcher::GetInstanceCallAt(caller_frame->pc(), caller_code,
- &old_data);
+ old_code ^= CodePatcher::GetInstanceCallAt(caller_frame->pc(),
+ caller_code, &old_data);
#endif
- SwitchableCallHandler handler(thread, receiver, arguments, caller_frame,
- caller_code, caller_function);
- handler.HandleMiss(old_data, old_code);
+ SwitchableCallHandler handler(thread, receiver, arguments, caller_frame,
+ caller_code, caller_function);
+ handler.HandleMiss(old_data, old_code);
+ },
+ /*use_force_growth=*/true);
}
// Handles interpreted interface call cache miss.
diff --git a/runtime/vm/runtime_entry.h b/runtime/vm/runtime_entry.h
index c29e6e4..83d8a63 100644
--- a/runtime/vm/runtime_entry.h
+++ b/runtime/vm/runtime_entry.h
@@ -11,6 +11,7 @@
#endif
#include "vm/flags.h"
#include "vm/heap/safepoint.h"
+#include "vm/log.h"
#include "vm/native_arguments.h"
#include "vm/runtime_entry_list.h"
diff --git a/runtime/vm/runtime_entry_x64.cc b/runtime/vm/runtime_entry_x64.cc
index 0ff88f0..caaa2a4 100644
--- a/runtime/vm/runtime_entry_x64.cc
+++ b/runtime/vm/runtime_entry_x64.cc
@@ -7,8 +7,10 @@
#include "vm/runtime_entry.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/assembler/assembler.h"
#include "vm/stub_code.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index f722603..99273cb 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -1,14 +1,15 @@
// Copyright (c) 2012, 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.
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/scopes.h"
+#include "vm/compiler/backend/slot.h"
#include "vm/object.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
-#if !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
diff --git a/runtime/vm/scopes.h b/runtime/vm/scopes.h
index d855c43..a592cd6 100644
--- a/runtime/vm/scopes.h
+++ b/runtime/vm/scopes.h
@@ -10,7 +10,6 @@
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/allocation.h"
-#include "vm/compiler/backend/slot.h"
#include "vm/growable_array.h"
#include "vm/object.h"
#include "vm/raw_object.h"
@@ -21,6 +20,7 @@
class CompileType;
class LocalScope;
+class Slot;
// Indices of [LocalVariable]s are abstract and have little todo with the
// actual frame layout!
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 181322e..4abd3ec 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -4324,6 +4324,17 @@
return true;
}
+static const MethodParameter* get_isolate_object_store_params[] = {
+ RUNNABLE_ISOLATE_PARAMETER,
+ NULL,
+};
+
+static bool GetIsolateObjectStore(Thread* thread, JSONStream* js) {
+ JSONObject jsobj(js);
+ thread->isolate()->isolate_object_store()->PrintToJSONObject(&jsobj);
+ return true;
+}
+
static const MethodParameter* get_class_list_params[] = {
RUNNABLE_ISOLATE_PARAMETER, NULL,
};
@@ -4820,6 +4831,8 @@
get_instances_params },
{ "getIsolate", GetIsolate,
get_isolate_params },
+ { "_getIsolateObjectStore", GetIsolateObjectStore,
+ get_isolate_object_store_params },
{ "getIsolateGroup", GetIsolateGroup,
get_isolate_group_params },
{ "getMemoryUsage", GetMemoryUsage,
diff --git a/runtime/vm/service_isolate.cc b/runtime/vm/service_isolate.cc
index 7e084ab..59aaf47 100644
--- a/runtime/vm/service_isolate.cc
+++ b/runtime/vm/service_isolate.cc
@@ -133,7 +133,7 @@
return isolate != nullptr && isolate == isolate_;
}
-bool ServiceIsolate::IsServiceIsolateDescendant(const Isolate* isolate) {
+bool ServiceIsolate::IsServiceIsolateDescendant(Isolate* isolate) {
MonitorLocker ml(monitor_);
return isolate->origin_id() == origin_;
}
diff --git a/runtime/vm/service_isolate.h b/runtime/vm/service_isolate.h
index a44b4b9..b8dd6b4 100644
--- a/runtime/vm/service_isolate.h
+++ b/runtime/vm/service_isolate.h
@@ -26,7 +26,7 @@
static bool Exists();
static bool IsRunning();
static bool IsServiceIsolate(const Isolate* isolate);
- static bool IsServiceIsolateDescendant(const Isolate* isolate);
+ static bool IsServiceIsolateDescendant(Isolate* isolate);
static Dart_Port Port();
static void WaitForServiceIsolateStartup();
@@ -97,9 +97,7 @@
static bool Exists() { return false; }
static bool IsRunning() { return false; }
static bool IsServiceIsolate(const Isolate* isolate) { return false; }
- static bool IsServiceIsolateDescendant(const Isolate* isolate) {
- return false;
- }
+ static bool IsServiceIsolateDescendant(Isolate* isolate) { return false; }
static void Run() {}
static bool SendIsolateStartupMessage() { return false; }
static bool SendIsolateShutdownMessage() { return false; }
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index ad55238..d966d45 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -621,7 +621,8 @@
intptr_t result_cid = result->GetClassId();
const auto unboxed_fields =
- isolate()->group()->class_table()->GetUnboxedFieldsMapAt(result_cid);
+ isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+ result_cid);
while (offset < next_field_offset) {
if (unboxed_fields.Get(offset / kWordSize)) {
@@ -1464,7 +1465,7 @@
WriteObjectImpl(cls, kAsInlinedObject);
const auto unboxed_fields =
- isolate()->group()->class_table()->GetUnboxedFieldsMapAt(
+ isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
cls->ptr()->id_);
// Write out all the fields for the object.
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index a795a0c..04309fe 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -5,9 +5,8 @@
#include "vm/stack_frame.h"
#include "platform/memory_sanitizer.h"
-#include "vm/compiler/assembler/assembler.h"
+#include "vm/code_descriptors.h"
#include "vm/compiler/runtime_api.h"
-#include "vm/deopt_instructions.h"
#include "vm/heap/become.h"
#include "vm/isolate.h"
#include "vm/object.h"
@@ -21,6 +20,10 @@
#include "vm/stub_code.h"
#include "vm/visitor.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/deopt_instructions.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
DECLARE_FLAG(bool, enable_interpreter);
@@ -847,6 +850,7 @@
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
+#if !defined(DART_PRECOMPILED_RUNTIME)
// Finds the potential offset for the current function's FP if the
// current frame were to be deoptimized.
intptr_t InlinedFunctionsIterator::GetDeoptFpOffset() const {
@@ -860,6 +864,7 @@
UNREACHABLE();
return 0;
}
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
#if defined(DEBUG)
void ValidateFrames() {
diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h
index f430e99..70fe3ed 100644
--- a/runtime/vm/stack_frame.h
+++ b/runtime/vm/stack_frame.h
@@ -431,7 +431,9 @@
return code_.raw();
}
+#if !defined(DART_PRECOMPILED_RUNTIME)
intptr_t GetDeoptFpOffset() const;
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
private:
void SetDone() { index_ = -1; }
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index 7643a8c..c0d55b8 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -4,11 +4,14 @@
#include "vm/stack_trace.h"
-#include "vm/compiler/frontend/bytecode_reader.h"
#include "vm/dart_api_impl.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/frontend/bytecode_reader.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
// Keep in sync with
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index 6ebb23a..21b71d5 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -7,8 +7,6 @@
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/clustered_snapshot.h"
-#include "vm/compiler/aot/precompiler.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/flags.h"
#include "vm/heap/safepoint.h"
@@ -18,6 +16,11 @@
#include "vm/virtual_memory.h"
#include "vm/visitor.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/aot/precompiler.h"
+#include "vm/compiler/assembler/assembler.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
DEFINE_FLAG(bool, disassemble_stubs, false, "Disassemble generated stubs.");
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index be2ad99..cffe96a 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -6,12 +6,15 @@
#define RUNTIME_VM_STUB_CODE_H_
#include "vm/allocation.h"
-#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/runtime_api.h"
-#include "vm/compiler/stub_code_compiler.h"
#include "vm/object.h"
#include "vm/stub_code_list.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/assembler/assembler.h"
+#include "vm/compiler/stub_code_compiler.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
// Forward declarations.
@@ -66,12 +69,14 @@
compiler::ObjectPoolBuilder* pool);
#endif
+#if !defined(DART_PRECOMPILED_RUNTIME)
// Generate the stub and finalize the generated code into the stub
// code executable area.
static RawCode* Generate(
const char* name,
compiler::ObjectPoolBuilder* object_pool_builder,
void (*GenerateStub)(compiler::Assembler* assembler));
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
static const Code& UnoptimizedStaticCallEntry(intptr_t num_args_tested);
diff --git a/runtime/vm/symbols.cc b/runtime/vm/symbols.cc
index 312e23b..bf4e645 100644
--- a/runtime/vm/symbols.cc
+++ b/runtime/vm/symbols.cc
@@ -581,12 +581,31 @@
table.Release();
}
if (symbol.IsNull()) {
+ IsolateGroup* group = thread->isolate_group();
Isolate* isolate = thread->isolate();
- SafepointMutexLocker ml(isolate->symbols_mutex());
- data = isolate->object_store()->symbol_table();
- SymbolTable table(&key, &value, &data);
- symbol ^= table.InsertNewOrGet(str);
- isolate->object_store()->set_symbol_table(table.Release());
+ // in JIT object_store lives on isolate, not on isolate group.
+ ObjectStore* object_store = group->object_store() == nullptr
+ ? isolate->object_store()
+ : group->object_store();
+ // in AOT no need to worry about background compiler, only about
+ // other mutators.
+#if defined(DART_PRECOMPILED_RUNTIME)
+ group->RunWithStoppedMutators(
+ [&]() {
+#else
+ SafepointRwLock* symbols_lock = group->object_store() == nullptr
+ ? isolate->symbols_lock()
+ : group->symbols_lock();
+ SafepointWriteRwLocker sl(thread, symbols_lock);
+#endif
+ data = object_store->symbol_table();
+ SymbolTable table(&key, &value, &data);
+ symbol ^= table.InsertNewOrGet(str);
+ object_store->set_symbol_table(table.Release());
+#if defined(DART_PRECOMPILED_RUNTIME)
+ },
+ /*use_force_growth=*/true);
+#endif
}
ASSERT(symbol.IsSymbol());
ASSERT(symbol.HasHash());
@@ -610,9 +629,21 @@
table.Release();
}
if (symbol.IsNull()) {
+ IsolateGroup* group = thread->isolate_group();
Isolate* isolate = thread->isolate();
- SafepointMutexLocker ml(isolate->symbols_mutex());
- data = isolate->object_store()->symbol_table();
+ // in JIT object_store lives on isolate, not on isolate group.
+ ObjectStore* object_store = group->object_store() == nullptr
+ ? isolate->object_store()
+ : group->object_store();
+ // in AOT no need to worry about background compiler, only about
+ // other mutators.
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ SafepointRwLock* symbols_lock = group->object_store() == nullptr
+ ? isolate->symbols_lock()
+ : group->symbols_lock();
+ SafepointReadRwLocker sl(thread, symbols_lock);
+#endif
+ data = object_store->symbol_table();
SymbolTable table(&key, &value, &data);
symbol ^= table.GetOrNull(str);
table.Release();
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index c69446c..420618d 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -5,7 +5,6 @@
#include "vm/thread.h"
#include "vm/dart_api_state.h"
-#include "vm/ffi_callback_trampolines.h"
#include "vm/growable_array.h"
#include "vm/heap/safepoint.h"
#include "vm/isolate.h"
@@ -25,6 +24,10 @@
#include "vm/timeline.h"
#include "vm/zone.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/ffi_callback_trampolines.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
+
namespace dart {
#if !defined(PRODUCT)
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 3fcd8b7..b55430a 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -4,10 +4,14 @@
#include "vm/type_testing_stubs.h"
#include "vm/compiler/assembler/disassembler.h"
+#include "vm/object_store.h"
+#include "vm/stub_code.h"
+#include "vm/timeline.h"
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/compiler/backend/il_printer.h"
-#include "vm/object_store.h"
-#include "vm/timeline.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
#define __ assembler->
@@ -580,6 +584,7 @@
#else // !defined(TARGET_ARCH_IA32)
+#if !defined(DART_PRECOMPILED_RUNTIME)
void RegisterTypeArgumentsUse(const Function& function,
TypeUsageInfo* type_usage_info,
const Class& klass,
@@ -587,6 +592,7 @@
// We only have a [TypeUsageInfo] object available durin AOT compilation.
UNREACHABLE();
}
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // !defined(TARGET_ARCH_IA32)
diff --git a/runtime/vm/type_testing_stubs.h b/runtime/vm/type_testing_stubs.h
index fa9bb07..2b1279c 100644
--- a/runtime/vm/type_testing_stubs.h
+++ b/runtime/vm/type_testing_stubs.h
@@ -5,8 +5,12 @@
#ifndef RUNTIME_VM_TYPE_TESTING_STUBS_H_
#define RUNTIME_VM_TYPE_TESTING_STUBS_H_
+#include "vm/object.h"
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/backend/il.h"
+#endif // !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
@@ -369,10 +373,12 @@
Class& klass_;
};
+#if !defined(DART_PRECOMPILED_RUNTIME)
void RegisterTypeArgumentsUse(const Function& function,
TypeUsageInfo* type_usage_info,
const Class& klass,
Definition* type_arguments);
+#endif
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index f23e5ee..29c3245 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -117,11 +117,12 @@
Isolate::FlagsInitialize(&api_flags);
Dart_Isolate isolate = NULL;
if (len == 0) {
- isolate =
- Dart_CreateIsolateGroup(name, NULL, data_buffer, instr_buffer,
- &api_flags, group_data, isolate_data, &err);
+ isolate = Dart_CreateIsolateGroup(
+ /*script_uri=*/name, /*name=*/name, data_buffer, instr_buffer,
+ &api_flags, group_data, isolate_data, &err);
} else {
- isolate = Dart_CreateIsolateGroupFromKernel(name, NULL, data_buffer, len,
+ isolate = Dart_CreateIsolateGroupFromKernel(/*script_uri=*/name,
+ /*name=*/name, data_buffer, len,
&api_flags, group_data,
isolate_data, &err);
}
@@ -157,6 +158,26 @@
RELEASE_ASSERT(!Dart_IsError(result));
}
+Dart_Isolate TestCase::CreateTestIsolateInGroup(const char* name,
+ Dart_Isolate parent,
+ void* group_data,
+ void* isolate_data) {
+ char* error;
+#if defined(DART_PRECOMPILED_RUNTIME)
+ Isolate* result = CreateWithinExistingIsolateGroupAOT(
+ reinterpret_cast<Isolate*>(parent)->group(), name, &error);
+#else
+ Isolate* result = CreateWithinExistingIsolateGroup(
+ reinterpret_cast<Isolate*>(parent)->group(), name, &error);
+#endif
+ if (error != nullptr) {
+ OS::PrintErr("CreateTestIsolateInGroup failed: %s\n", error);
+ free(error);
+ }
+ EXPECT(result != nullptr);
+ return Api::CastIsolate(result);
+}
+
struct TestLibEntry {
const char* url;
const char* source;
diff --git a/runtime/vm/unit_test.h b/runtime/vm/unit_test.h
index 7dce889..21fc9cb 100644
--- a/runtime/vm/unit_test.h
+++ b/runtime/vm/unit_test.h
@@ -361,6 +361,11 @@
static Dart_Isolate CreateTestIsolate(const char* name = nullptr,
void* isolate_group_data = nullptr,
void* isolate_data = nullptr);
+ static Dart_Isolate CreateTestIsolateInGroup(const char* name,
+ Dart_Isolate parent,
+ void* group_data = nullptr,
+ void* isolate_data = nullptr);
+
static Dart_Handle library_handler(Dart_LibraryTag tag,
Dart_Handle library,
Dart_Handle url);
diff --git a/runtime/vm/visitor.cc b/runtime/vm/visitor.cc
index 8274cc8..145dd06 100644
--- a/runtime/vm/visitor.cc
+++ b/runtime/vm/visitor.cc
@@ -11,6 +11,6 @@
ObjectPointerVisitor::ObjectPointerVisitor(IsolateGroup* isolate_group)
: isolate_group_(isolate_group),
gc_root_type_("unknown"),
- shared_class_table_(isolate_group->class_table()) {}
+ shared_class_table_(isolate_group->shared_class_table()) {}
} // namespace dart
diff --git a/sdk_nnbd/lib/_internal/js_dev_runtime/patch/collection_patch.dart b/sdk_nnbd/lib/_internal/js_dev_runtime/patch/collection_patch.dart
index c10f04f..d9709b9 100644
--- a/sdk_nnbd/lib/_internal/js_dev_runtime/patch/collection_patch.dart
+++ b/sdk_nnbd/lib/_internal/js_dev_runtime/patch/collection_patch.dart
@@ -578,30 +578,3 @@
iterator);
}
}
-
-@patch
-abstract class _SplayTree<K, Node extends _SplayTreeNode<K>> {
- @patch
- Node _splayMin(Node node) {
- var current = node;
- while (current.left != null) {
- var left = current.left!;
- current.left = left.right;
- left.right = current;
- current = left as Node;
- }
- return current;
- }
-
- @patch
- Node _splayMax(Node node) {
- var current = node;
- while (current.right != null) {
- var right = current.right!;
- current.right = right.left;
- right.left = current;
- current = right as Node;
- }
- return current;
- }
-}
diff --git a/sdk_nnbd/lib/_internal/js_runtime/lib/collection_patch.dart b/sdk_nnbd/lib/_internal/js_runtime/lib/collection_patch.dart
index 592b8c2..2b8bd25 100644
--- a/sdk_nnbd/lib/_internal/js_runtime/lib/collection_patch.dart
+++ b/sdk_nnbd/lib/_internal/js_runtime/lib/collection_patch.dart
@@ -1714,30 +1714,3 @@
}
}
}
-
-@patch
-abstract class _SplayTree<K, Node extends _SplayTreeNode<K>> {
- @patch
- Node _splayMin(Node node) {
- Node current = node;
- while (current.left != null) {
- Node left = current.left as Node;
- current.left = left.right;
- left.right = current;
- current = left;
- }
- return current;
- }
-
- @patch
- Node _splayMax(Node node) {
- Node current = node;
- while (current.right != null) {
- Node right = current.right as Node;
- current.right = right.left;
- right.left = current;
- current = right;
- }
- return current;
- }
-}
diff --git a/sdk_nnbd/lib/_internal/vm/lib/collection_patch.dart b/sdk_nnbd/lib/_internal/vm/lib/collection_patch.dart
index dcb8fa1..1307fb7 100644
--- a/sdk_nnbd/lib/_internal/vm/lib/collection_patch.dart
+++ b/sdk_nnbd/lib/_internal/vm/lib/collection_patch.dart
@@ -13,6 +13,10 @@
import "dart:typed_data" show Uint32List;
+class _TypeTest<T> {
+ bool test(v) => v is T;
+}
+
/// These are the additional parts of this patch library:
// part "compact_hash.dart";
@@ -915,35 +919,3 @@
@patch
factory LinkedHashSet.identity() => new _CompactLinkedIdentityHashSet<E>();
}
-
-@patch
-abstract class _SplayTree<K, Node extends _SplayTreeNode<K>> {
- // We override _splayMin and _splayMax to optimize type-checks.
- @patch
- Node _splayMin(Node node) {
- Node current = node;
- Object? nextLeft = current.left;
- while (nextLeft != null) {
- Node left = internal.unsafeCast<Node>(nextLeft);
- current.left = left.right;
- left.right = current;
- current = left;
- nextLeft = current.left;
- }
- return current;
- }
-
- @patch
- Node _splayMax(Node node) {
- Node current = node;
- Object? nextRight = current.right;
- while (nextRight != null) {
- Node right = internal.unsafeCast<Node>(nextRight);
- current.right = right.left;
- right.left = current;
- current = right;
- nextRight = current.right;
- }
- return current;
- }
-}
diff --git a/sdk_nnbd/lib/async/future.dart b/sdk_nnbd/lib/async/future.dart
index 30748ca..3de7374 100644
--- a/sdk_nnbd/lib/async/future.dart
+++ b/sdk_nnbd/lib/async/future.dart
@@ -311,7 +311,7 @@
* later time that isn't necessarily after a known fixed duration.
*/
factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) {
- if (computation == null && null is! T) {
+ if (computation == null && const <Null>[] is! List<T>) {
throw ArgumentError.value(
null, "computation", "The type parameter is not nullable");
}
diff --git a/sdk_nnbd/lib/async/stream.dart b/sdk_nnbd/lib/async/stream.dart
index b0d41f4..241c706 100644
--- a/sdk_nnbd/lib/async/stream.dart
+++ b/sdk_nnbd/lib/async/stream.dart
@@ -263,7 +263,7 @@
*/
factory Stream.periodic(Duration period,
[T computation(int computationCount)?]) {
- if (computation == null && null is! T) {
+ if (computation == null && const <Null>[] is! List<T>) {
throw ArgumentError.value(null, "computation",
"Must not be omitted when the event type is non-nullable");
}
diff --git a/sdk_nnbd/lib/collection/splay_tree.dart b/sdk_nnbd/lib/collection/splay_tree.dart
index b81a034..a019b64 100644
--- a/sdk_nnbd/lib/collection/splay_tree.dart
+++ b/sdk_nnbd/lib/collection/splay_tree.dart
@@ -8,36 +8,27 @@
/// A node in a splay tree. It holds the sorting key and the left
/// and right children in the tree.
-class _SplayTreeNode<K> {
- // The key is nullable to be able to create a dummy node.
- final K? _key;
+class _SplayTreeNode<K, Node extends _SplayTreeNode<K, Node>> {
+ final K key;
- _SplayTreeNode<K>? left;
- _SplayTreeNode<K>? right;
+ Node? left;
+ Node? right;
- _SplayTreeNode(this._key);
+ _SplayTreeNode(this.key);
+}
- K get key {
- // TODO(dartbug.com/40892): replace with '_key as K'
- K? localKey = _key;
- return (localKey != null) ? localKey : localKey as K;
- }
+/// A node in a splay tree based set.
+class _SplayTreeSetNode<K> extends _SplayTreeNode<K, _SplayTreeSetNode<K>> {
+ _SplayTreeSetNode(K key) : super(key);
}
/// A node in a splay tree based map.
///
/// A [_SplayTreeNode] that also contains a value
-class _SplayTreeMapNode<K, V> extends _SplayTreeNode<K> {
- // The value is nullable to be able to create a dummy node.
- V? _value;
-
- _SplayTreeMapNode(K? key, this._value) : super(key);
-
- V get value {
- // TODO(dartbug.com/40892): replace with '_value as V'
- V? localValue = _value;
- return (localValue != null) ? localValue : localValue as V;
- }
+class _SplayTreeMapNode<K, V>
+ extends _SplayTreeNode<K, _SplayTreeMapNode<K, V>> {
+ V value;
+ _SplayTreeMapNode(K key, this.value) : super(key);
}
/// A splay tree is a self-balancing binary search tree.
@@ -46,16 +37,12 @@
/// are quick to access again.
/// It performs basic operations such as insertion, look-up and
/// removal, in O(log(n)) amortized time.
-abstract class _SplayTree<K, Node extends _SplayTreeNode<K>> {
+abstract class _SplayTree<K, Node extends _SplayTreeNode<K, Node>> {
// The root node of the splay tree. It will contain either the last
// element inserted or the last element looked up.
Node? get _root;
set _root(Node? newValue);
- // The dummy node used when performing a splay on the tree. Reusing it
- // avoids allocating a node each time a splay is performed.
- Node get _dummy;
-
// Number of elements in the splay tree.
int _count = 0;
@@ -71,14 +58,11 @@
int _splayCount = 0;
/// The comparator that is used for this splay tree.
- Comparator<K> get _comparator;
+ Comparator<K> get _compare;
/// The predicate to determine that a given object is a valid key.
_Predicate get _validKey;
- /// Comparison used to compare keys.
- int _compare(K key1, K key2);
-
/// Perform the splay operation for the given key. Moves the node with
/// the given key to the top of the tree. If no node has the given
/// key, the last node on the search path is moved to the top of the
@@ -90,59 +74,80 @@
int _splay(K key) {
if (_root == null) return -1;
- // The right child of the dummy node will hold
- // the L tree of the algorithm. The left child of the dummy node
- // will hold the R tree of the algorithm. Using a dummy node, left
- // and right will always be nodes and we avoid special cases.
- Node left = _dummy;
- Node right = _dummy;
- Node current = _root!;
+ // The right and newTreeRight variables start out null, and are set
+ // after the first move left. The right node is the destination
+ // for subsequent left rebalances, and newTreeRight holds the left
+ // child of the final tree. The newTreeRight variable is set at most
+ // once, after the first move left, and is null iff right is null.
+ // The left and newTreeLeft variables play the corresponding role for
+ // right rebalances.
+ Node? right;
+ Node? newTreeRight;
+ Node? left;
+ Node? newTreeLeft;
+ var current = _root!;
+ // Hoist the field read out of the loop.
+ var compare = _compare;
int comp;
while (true) {
- comp = _compare(current.key, key);
+ comp = compare(current.key, key);
if (comp > 0) {
- if (current.left == null) break;
- comp = _compare(current.left!.key, key);
+ var currentLeft = current.left;
+ if (currentLeft == null) break;
+ comp = compare(currentLeft.key, key);
if (comp > 0) {
// Rotate right.
- Node tmp = current.left as Node;
- current.left = tmp.right;
- tmp.right = current;
- current = tmp;
- if (current.left == null) break;
+ current.left = currentLeft.right;
+ currentLeft.right = current;
+ current = currentLeft;
+ currentLeft = current.left;
+ if (currentLeft == null) break;
}
// Link right.
- right.left = current;
+ if (right == null) {
+ // First left rebalance, store the eventual right child
+ newTreeRight = current;
+ } else {
+ right.left = current;
+ }
right = current;
- current = current.left as Node;
+ current = currentLeft;
} else if (comp < 0) {
- if (current.right == null) break;
- comp = _compare(current.right!.key, key);
+ var currentRight = current.right;
+ if (currentRight == null) break;
+ comp = compare(currentRight.key, key);
if (comp < 0) {
// Rotate left.
- Node tmp = current.right as Node;
- current.right = tmp.left;
- tmp.left = current;
- current = tmp;
- if (current.right == null) break;
+ current.right = currentRight.left;
+ currentRight.left = current;
+ current = currentRight;
+ currentRight = current.right;
+ if (currentRight == null) break;
}
// Link left.
- left.right = current;
+ if (left == null) {
+ // First right rebalance, store the eventual left child
+ newTreeLeft = current;
+ } else {
+ left.right = current;
+ }
left = current;
- current = current.right as Node;
+ current = currentRight;
} else {
break;
}
}
// Assemble.
- left.right = current.left;
- right.left = current.right;
- current.left = _dummy.right;
- current.right = _dummy.left;
+ if (left != null) {
+ left.right = current.left;
+ current.left = newTreeLeft;
+ }
+ if (right != null) {
+ right.left = current.right;
+ current.right = newTreeRight;
+ }
_root = current;
- _dummy.right = null;
- _dummy.left = null;
_splayCount++;
return comp;
}
@@ -151,31 +156,57 @@
// anchored at [node].
// and that node is returned. It should replace the reference to [node]
// in any parent tree or root pointer.
- external Node _splayMin(Node node);
+ Node _splayMin(Node node) {
+ var current = node;
+ var nextLeft = current.left;
+ while (nextLeft != null) {
+ var left = nextLeft;
+ current.left = left.right;
+ left.right = current;
+ current = left;
+ nextLeft = current.left;
+ }
+ return current;
+ }
// Emulates splaying with a key that is greater than any in the subtree
// anchored at [node].
// After this, the largest element in the tree is the root of the subtree,
// and that node is returned. It should replace the reference to [node]
// in any parent tree or root pointer.
- external Node _splayMax(Node node);
+ Node _splayMax(Node node) {
+ var current = node;
+ var nextRight = current.right;
+ while (nextRight != null) {
+ var right = nextRight;
+ current.right = right.left;
+ right.left = current;
+ current = right;
+ nextRight = current.right;
+ }
+ return current;
+ }
Node? _remove(K key) {
if (_root == null) return null;
int comp = _splay(key);
if (comp != 0) return null;
- Node result = _root!;
+ var root = _root!;
+ var result = root;
+ var left = root.left;
_count--;
// assert(_count >= 0);
- if (_root!.left == null) {
- _root = _root!.right as Node?;
+ if (left == null) {
+ _root = root.right;
} else {
- Node? right = _root!.right as Node?;
+ var right = root.right;
// Splay to make sure that the new root has an empty right child.
- _root = _splayMax(_root!.left as Node);
+ root = _splayMax(left);
+
// Insert the original right child as the right child of the new
// root.
- _root!.right = right;
+ root.right = right;
+ _root = root;
}
_modificationCount++;
return result;
@@ -188,32 +219,35 @@
void _addNewRoot(Node node, int comp) {
_count++;
_modificationCount++;
- if (_root == null) {
+ var root = _root;
+ if (root == null) {
_root = node;
return;
}
// assert(_count >= 0);
if (comp < 0) {
- node.left = _root;
- node.right = _root!.right;
- _root!.right = null;
+ node.left = root;
+ node.right = root.right;
+ root.right = null;
} else {
- node.right = _root;
- node.left = _root!.left;
- _root!.left = null;
+ node.right = root;
+ node.left = root.left;
+ root.left = null;
}
_root = node;
}
Node? get _first {
- if (_root == null) return null;
- _root = _splayMin(_root!);
+ var root = _root;
+ if (root == null) return null;
+ _root = _splayMin(root);
return _root;
}
Node? get _last {
- if (_root == null) return null;
- _root = _splayMax(_root!);
+ var root = _root;
+ if (root == null) return null;
+ _root = _splayMax(root);
return _root;
}
@@ -224,19 +258,14 @@
}
}
-class _TypeTest<T> {
- bool test(v) => v is T;
-}
-
-int _dynamicCompare(dynamic a, dynamic b) =>
- Comparable.compare(a as Comparable, b as Comparable);
+int _dynamicCompare(dynamic a, dynamic b) => Comparable.compare(a, b);
Comparator<K> _defaultCompare<K>() {
// If K <: Comparable, then we can just use Comparable.compare
// with no casts.
Object compare = Comparable.compare;
if (compare is Comparator<K>) {
- return compare as Comparator<K>;
+ return compare;
}
// Otherwise wrap and cast the arguments on each call.
return _dynamicCompare;
@@ -266,15 +295,14 @@
class SplayTreeMap<K, V> extends _SplayTree<K, _SplayTreeMapNode<K, V>>
with MapMixin<K, V> {
_SplayTreeMapNode<K, V>? _root;
- final _SplayTreeMapNode<K, V> _dummy = _SplayTreeMapNode<K, V>(null, null);
- Comparator<K> _comparator;
+ Comparator<K> _compare;
_Predicate _validKey;
SplayTreeMap(
[int Function(K key1, K key2)? compare,
bool Function(dynamic potentialKey)? isValidKey])
- : _comparator = compare ?? _defaultCompare<K>(),
+ : _compare = compare ?? _defaultCompare<K>(),
_validKey = isValidKey ?? ((dynamic v) => v is K);
/// Creates a [SplayTreeMap] that contains all key/value pairs of [other].
@@ -284,9 +312,12 @@
factory SplayTreeMap.from(Map<dynamic, dynamic> other,
[int Function(K key1, K key2)? compare,
bool Function(dynamic potentialKey)? isValidKey]) {
+ if (other is Map<K, V>) {
+ return SplayTreeMap<K, V>.of(other, compare, isValidKey);
+ }
SplayTreeMap<K, V> result = SplayTreeMap<K, V>(compare, isValidKey);
other.forEach((dynamic k, dynamic v) {
- result[k as K] = v as V;
+ result[k] = v;
});
return result;
}
@@ -335,12 +366,10 @@
return map;
}
- int _compare(K key1, K key2) => _comparator(key1, key2);
-
V? operator [](Object? key) {
if (!_validKey(key)) return null;
if (_root != null) {
- int comp = _splay(key as K);
+ int comp = _splay(key as dynamic);
if (comp == 0) {
return _root!.value;
}
@@ -350,7 +379,7 @@
V? remove(Object? key) {
if (!_validKey(key)) return null;
- _SplayTreeMapNode<K, V>? mapRoot = _remove(key as K);
+ _SplayTreeMapNode<K, V>? mapRoot = _remove(key as dynamic);
if (mapRoot != null) return mapRoot.value;
return null;
}
@@ -361,7 +390,7 @@
// the key to the root of the tree.
int comp = _splay(key);
if (comp == 0) {
- _root!._value = value;
+ _root!.value = value;
return;
}
_addNewRoot(_SplayTreeMapNode(key, value), comp);
@@ -401,9 +430,10 @@
bool get isNotEmpty => !isEmpty;
void forEach(void f(K key, V value)) {
- Iterator<_SplayTreeNode<K>> nodes = _SplayTreeNodeIterator<K>(this);
+ Iterator<_SplayTreeMapNode<K, V>> nodes =
+ _SplayTreeNodeIterator<K, _SplayTreeMapNode<K, V>>(this);
while (nodes.moveNext()) {
- _SplayTreeMapNode<K, V> node = nodes.current as _SplayTreeMapNode<K, V>;
+ _SplayTreeMapNode<K, V> node = nodes.current;
f(node.key, node.value);
}
}
@@ -417,21 +447,21 @@
}
bool containsKey(Object? key) {
- return _validKey(key) && _splay(key as K) == 0;
+ return _validKey(key) && _splay(key as dynamic) == 0;
}
bool containsValue(Object? value) {
int initialSplayCount = _splayCount;
- bool visit(_SplayTreeMapNode? node) {
+ bool visit(_SplayTreeMapNode<K, V>? node) {
while (node != null) {
if (node.value == value) return true;
if (initialSplayCount != _splayCount) {
throw ConcurrentModificationError(this);
}
- if (node.right != null && visit(node.right as _SplayTreeMapNode)) {
+ if (node.right != null && visit(node.right)) {
return true;
}
- node = node.left as _SplayTreeMapNode?;
+ node = node.left;
}
return false;
}
@@ -439,7 +469,8 @@
return visit(_root);
}
- Iterable<K> get keys => _SplayTreeKeyIterable<K>(this);
+ Iterable<K> get keys =>
+ _SplayTreeKeyIterable<K, _SplayTreeMapNode<K, V>>(this);
Iterable<V> get values => _SplayTreeValueIterable<K, V>(this);
@@ -462,12 +493,14 @@
if (_root == null) return null;
int comp = _splay(key);
if (comp < 0) return _root!.key;
- _SplayTreeNode<K>? node = _root!.left;
+ _SplayTreeMapNode<K, V>? node = _root!.left;
if (node == null) return null;
- while (node!.right != null) {
- node = node.right;
+ var nodeRight = node.right;
+ while (nodeRight != null) {
+ node = nodeRight;
+ nodeRight = node.right;
}
- return node.key;
+ return node!.key;
}
/// Get the first key in the map that is strictly larger than [key]. Returns
@@ -477,17 +510,20 @@
if (_root == null) return null;
int comp = _splay(key);
if (comp > 0) return _root!.key;
- _SplayTreeNode<K>? node = _root!.right;
+ _SplayTreeMapNode<K, V>? node = _root!.right;
if (node == null) return null;
- while (node!.left != null) {
- node = node.left;
+ var nodeLeft = node.left;
+ while (nodeLeft != null) {
+ node = nodeLeft;
+ nodeLeft = node.left;
}
- return node.key;
+ return node!.key;
}
}
-abstract class _SplayTreeIterator<K, T> implements Iterator<T> {
- final _SplayTree<K, _SplayTreeNode<K>> _tree;
+abstract class _SplayTreeIterator<K, Node extends _SplayTreeNode<K, Node>, T>
+ implements Iterator<T> {
+ final _SplayTree<K, Node> _tree;
/// Worklist of nodes to visit.
///
@@ -497,7 +533,7 @@
/// the nodes of a full traversal.
///
/// Only valid as long as the original tree isn't reordered.
- final List<_SplayTreeNode<K>> _workList = <_SplayTreeNode<K>>[];
+ final List<Node> _workList = [];
/// Original modification counter of [_tree].
///
@@ -514,16 +550,16 @@
int _splayCount;
/// Current node.
- _SplayTreeNode<K>? _currentNode;
+ Node? _currentNode;
- _SplayTreeIterator(_SplayTree<K, _SplayTreeNode<K>> tree)
+ _SplayTreeIterator(_SplayTree<K, Node> tree)
: _tree = tree,
_modificationCount = tree._modificationCount,
_splayCount = tree._splayCount {
_findLeftMostDescendent(tree._root);
}
- _SplayTreeIterator.startAt(_SplayTree<K, _SplayTreeNode<K>> tree, K startKey)
+ _SplayTreeIterator.startAt(_SplayTree<K, Node> tree, K startKey)
: _tree = tree,
_modificationCount = tree._modificationCount,
_splayCount = -1 {
@@ -539,11 +575,12 @@
}
T get current {
- if (_currentNode == null) return null as T;
- return _getValue(_currentNode!);
+ var node = _currentNode;
+ if (node == null) return null as T;
+ return _getValue(node);
}
- void _findLeftMostDescendent(_SplayTreeNode<K>? node) {
+ void _findLeftMostDescendent(Node? node) {
while (node != null) {
_workList.add(node);
node = node.left;
@@ -556,16 +593,12 @@
/// If the key-set changes, iteration is aborted before getting
/// here, so we know that the keys are the same as before, it's
/// only the tree that has been reordered.
- void _rebuildWorkList(_SplayTreeNode<K> currentNode) {
+ void _rebuildWorkList(Node currentNode) {
assert(_workList.isNotEmpty);
_workList.clear();
- if (currentNode == null) {
- _findLeftMostDescendent(_tree._root);
- } else {
- _tree._splay(currentNode.key);
- _findLeftMostDescendent(_tree._root!.right);
- assert(_workList.isNotEmpty);
- }
+ _tree._splay(currentNode.key);
+ _findLeftMostDescendent(_tree._root!.right);
+ assert(_workList.isNotEmpty);
}
bool moveNext() {
@@ -589,20 +622,21 @@
return true;
}
- T _getValue(_SplayTreeNode<K> node);
+ T _getValue(Node node);
}
-class _SplayTreeKeyIterable<K> extends EfficientLengthIterable<K> {
- _SplayTree<K, _SplayTreeNode<K>> _tree;
+class _SplayTreeKeyIterable<K, Node extends _SplayTreeNode<K, Node>>
+ extends EfficientLengthIterable<K> {
+ _SplayTree<K, Node> _tree;
_SplayTreeKeyIterable(this._tree);
int get length => _tree._count;
bool get isEmpty => _tree._count == 0;
- Iterator<K> get iterator => _SplayTreeKeyIterator<K>(_tree);
+ Iterator<K> get iterator => _SplayTreeKeyIterator<K, Node>(_tree);
Set<K> toSet() {
- SplayTreeSet<K> set = SplayTreeSet<K>(_tree._comparator, _tree._validKey);
+ SplayTreeSet<K> set = SplayTreeSet<K>(_tree._compare, _tree._validKey);
set._count = _tree._count;
- set._root = set._copyNode(_tree._root);
+ set._root = set._copyNode<Node>(_tree._root);
return set;
}
}
@@ -615,26 +649,24 @@
Iterator<V> get iterator => _SplayTreeValueIterator<K, V>(_map);
}
-class _SplayTreeKeyIterator<K> extends _SplayTreeIterator<K, K> {
- _SplayTreeKeyIterator(_SplayTree<K, _SplayTreeNode<K>> map) : super(map);
- K _getValue(_SplayTreeNode<K> node) => node.key;
+class _SplayTreeKeyIterator<K, Node extends _SplayTreeNode<K, Node>>
+ extends _SplayTreeIterator<K, Node, K> {
+ _SplayTreeKeyIterator(_SplayTree<K, Node> map) : super(map);
+ K _getValue(Node node) => node.key;
}
-class _SplayTreeValueIterator<K, V> extends _SplayTreeIterator<K, V> {
+class _SplayTreeValueIterator<K, V>
+ extends _SplayTreeIterator<K, _SplayTreeMapNode<K, V>, V> {
_SplayTreeValueIterator(SplayTreeMap<K, V> map) : super(map);
- V _getValue(_SplayTreeNode<K> node) {
- _SplayTreeMapNode<K, V> mapNode = node as _SplayTreeMapNode<K, V>;
- return mapNode.value;
- }
+ V _getValue(_SplayTreeMapNode<K, V> node) => node.value;
}
-class _SplayTreeNodeIterator<K>
- extends _SplayTreeIterator<K, _SplayTreeNode<K>> {
- _SplayTreeNodeIterator(_SplayTree<K, _SplayTreeNode<K>> tree) : super(tree);
- _SplayTreeNodeIterator.startAt(
- _SplayTree<K, _SplayTreeNode<K>> tree, K startKey)
+class _SplayTreeNodeIterator<K, Node extends _SplayTreeNode<K, Node>>
+ extends _SplayTreeIterator<K, Node, Node> {
+ _SplayTreeNodeIterator(_SplayTree<K, Node> tree) : super(tree);
+ _SplayTreeNodeIterator.startAt(_SplayTree<K, Node> tree, K startKey)
: super.startAt(tree, startKey);
- _SplayTreeNode<K> _getValue(_SplayTreeNode<K> node) => node;
+ Node _getValue(Node node) => node;
}
/// A [Set] of objects that can be ordered relative to each other.
@@ -651,12 +683,11 @@
/// [Comparable], and are compared using their [Comparable.compareTo] method.
/// Non-comparable objects (including `null`) will not work as an element
/// in that case.
-class SplayTreeSet<E> extends _SplayTree<E, _SplayTreeNode<E>>
+class SplayTreeSet<E> extends _SplayTree<E, _SplayTreeSetNode<E>>
with IterableMixin<E>, SetMixin<E> {
- _SplayTreeNode<E>? _root;
- final _SplayTreeNode<E> _dummy = _SplayTreeNode<E>(null);
+ _SplayTreeSetNode<E>? _root;
- Comparator<E> _comparator;
+ Comparator<E> _compare;
_Predicate _validKey;
/// Create a new [SplayTreeSet] with the given compare function.
@@ -684,7 +715,7 @@
SplayTreeSet(
[int Function(E key1, E key2)? compare,
bool Function(dynamic potentialKey)? isValidKey])
- : _comparator = compare ?? _defaultCompare<E>(),
+ : _compare = compare ?? _defaultCompare<E>(),
_validKey = isValidKey ?? ((dynamic v) => v is E);
/// Creates a [SplayTreeSet] that contains all [elements].
@@ -703,10 +734,12 @@
factory SplayTreeSet.from(Iterable elements,
[int Function(E key1, E key2)? compare,
bool Function(dynamic potentialKey)? isValidKey]) {
+ if (elements is Iterable<E>) {
+ return SplayTreeSet<E>.of(elements, compare, isValidKey);
+ }
SplayTreeSet<E> result = SplayTreeSet<E>(compare, isValidKey);
- for (final element in elements) {
- E e = element as E;
- result.add(e);
+ for (var element in elements) {
+ result.add(element as dynamic);
}
return result;
}
@@ -722,14 +755,14 @@
SplayTreeSet(compare, isValidKey)..addAll(elements);
Set<T> _newSet<T>() =>
- SplayTreeSet<T>((T a, T b) => _comparator(a as E, b as E), _validKey);
+ SplayTreeSet<T>((T a, T b) => _compare(a as E, b as E), _validKey);
Set<R> cast<R>() => Set.castFrom<E, R>(this, newSet: _newSet);
- int _compare(E e1, E e2) => _comparator(e1, e2);
// From Iterable.
- Iterator<E> get iterator => _SplayTreeKeyIterator<E>(this);
+ Iterator<E> get iterator =>
+ _SplayTreeKeyIterator<E, _SplayTreeSetNode<E>>(this);
int get length => _count;
bool get isEmpty => _root == null;
@@ -759,7 +792,7 @@
bool add(E element) {
int compare = _splay(element);
if (compare == 0) return false;
- _addNewRoot(_SplayTreeNode(element), compare);
+ _addNewRoot(_SplayTreeSetNode(element), compare);
return true;
}
@@ -772,7 +805,7 @@
for (E element in elements) {
int compare = _splay(element);
if (compare != 0) {
- _addNewRoot(_SplayTreeNode(element), compare);
+ _addNewRoot(_SplayTreeSetNode(element), compare);
}
}
}
@@ -785,7 +818,7 @@
void retainAll(Iterable<Object?> elements) {
// Build a set with the same sense of equality as this set.
- SplayTreeSet<E> retainSet = SplayTreeSet<E>(_comparator, _validKey);
+ SplayTreeSet<E> retainSet = SplayTreeSet<E>(_compare, _validKey);
int modificationCount = _modificationCount;
for (Object? object in elements) {
if (modificationCount != _modificationCount) {
@@ -813,7 +846,7 @@
}
Set<E> intersection(Set<Object?> other) {
- Set<E> result = SplayTreeSet<E>(_comparator, _validKey);
+ Set<E> result = SplayTreeSet<E>(_compare, _validKey);
for (E element in this) {
if (other.contains(element)) result.add(element);
}
@@ -821,7 +854,7 @@
}
Set<E> difference(Set<Object?> other) {
- Set<E> result = SplayTreeSet<E>(_comparator, _validKey);
+ Set<E> result = SplayTreeSet<E>(_compare, _validKey);
for (E element in this) {
if (!other.contains(element)) result.add(element);
}
@@ -833,19 +866,46 @@
}
SplayTreeSet<E> _clone() {
- var set = SplayTreeSet<E>(_comparator, _validKey);
+ var set = SplayTreeSet<E>(_compare, _validKey);
set._count = _count;
- set._root = _copyNode(_root);
+ set._root = _copyNode<_SplayTreeSetNode<E>>(_root);
return set;
}
// Copies the structure of a SplayTree into a new similar structure.
// Works on _SplayTreeMapNode as well, but only copies the keys,
- _SplayTreeNode<E>? _copyNode(_SplayTreeNode<E>? node) {
+ _SplayTreeSetNode<E>? _copyNode<Node extends _SplayTreeNode<E, Node>>(
+ Node? node) {
if (node == null) return null;
- return _SplayTreeNode<E>(node.key)
- ..left = _copyNode(node.left)
- ..right = _copyNode(node.right);
+ // Given a source node and a destination node, copy the left
+ // and right subtrees of the source node into the destination node.
+ // The left subtree is copied recursively, but the right spine
+ // of every subtree is copied iteratively.
+ void copyChildren(Node node, _SplayTreeSetNode<E> dest) {
+ Node? left;
+ Node? right;
+ do {
+ left = node.left;
+ right = node.right;
+ if (left != null) {
+ var newLeft = _SplayTreeSetNode<E>(left.key);
+ dest.left = newLeft;
+ // Recursively copy the left tree.
+ copyChildren(left, newLeft);
+ }
+ if (right != null) {
+ var newRight = _SplayTreeSetNode<E>(right.key);
+ dest.right = newRight;
+ // Set node and dest to copy the right tree iteratively.
+ node = right;
+ dest = newRight;
+ }
+ } while (right != null);
+ }
+
+ var result = _SplayTreeSetNode<E>(node.key);
+ copyChildren(node, result);
+ return result;
}
void clear() {
diff --git a/tests/language/nnbd/try_catch/default_catch_type_error_test.dart b/tests/language/nnbd/try_catch/default_catch_type_error_test.dart
new file mode 100644
index 0000000..73b3b94
--- /dev/null
+++ b/tests/language/nnbd/try_catch/default_catch_type_error_test.dart
@@ -0,0 +1,15 @@
+// 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.
+
+void main() {
+ try {} catch (error) {
+ error.notAMethodOnObject();
+ // ^^^^^^^^^^^^^^^^^^
+ // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_METHOD
+ // [cfe] The method 'notAMethodOnObject' isn't defined for the class 'Object'.
+ _takesObject(error);
+ }
+}
+
+void _takesObject(Object o) {}
diff --git a/tests/lib/async/stream_controller_async_test.dart b/tests/lib/async/stream_controller_async_test.dart
index 70223fe..dc83af6 100644
--- a/tests/lib/async/stream_controller_async_test.dart
+++ b/tests/lib/async/stream_controller_async_test.dart
@@ -33,9 +33,9 @@
test("StreamController.fold throws", () {
StreamController c = new StreamController();
Stream stream = c.stream.asBroadcastStream(onCancel: cancelSub);
- stream.fold(0, (a, b) {
+ Future<int?>.value(stream.fold(0, (a, b) {
throw "Fnyf!";
- }).catchError(expectAsync((error) {
+ })).catchError(expectAsync((error) {
Expect.equals("Fnyf!", error);
}));
c.add(42);
@@ -57,9 +57,9 @@
test("Single-subscription StreamController.fold throws", () {
StreamController c = new StreamController();
Stream stream = c.stream;
- stream.fold(0, (a, b) {
+ Future<int?>.value(stream.fold(0, (a, b) {
throw "Fnyf!";
- }).catchError(expectAsync((e) {
+ })).catchError(expectAsync((e) {
Expect.equals("Fnyf!", e);
}));
c.add(42);
@@ -669,8 +669,8 @@
});
}
-void testSink({
- required bool sync, required bool broadcast, required bool asBroadcast}) {
+void testSink(
+ {required bool sync, required bool broadcast, required bool asBroadcast}) {
String type =
"${sync ? "S" : "A"}${broadcast ? "B" : "S"}${asBroadcast ? "aB" : ""}";
test("$type-controller-sink", () {
diff --git a/tests/lib/async/stream_join_test.dart b/tests/lib/async/stream_join_test.dart
index 6e3a98b..49077aa 100644
--- a/tests/lib/async/stream_join_test.dart
+++ b/tests/lib/async/stream_join_test.dart
@@ -52,8 +52,7 @@
test("join-error", () {
StreamController c = new StreamController();
- c.stream
- .join("X")
+ Future<String?>.value(c.stream.join("X"))
.catchError(expectAsync((String s) => expect(s, equals("BAD!"))));
c.add(new Foo("foo"));
c.add(new Foo("bar"));
diff --git a/tests/lib/isolate/message3_test.dart b/tests/lib/isolate/message3_test.dart
index 0bc63ff..f1f11bf 100644
--- a/tests/lib/isolate/message3_test.dart
+++ b/tests/lib/isolate/message3_test.dart
@@ -60,7 +60,11 @@
}
class E {
+ // Make sure E.fun is not removed by the tree shaker, as this test verifies
+ // that an object with a tear-off in E.fun cannot be sent.
+ @pragma("vm:entry-point")
Function fun;
+
E(this.fun);
static fooFun() => 499;
diff --git a/tests/lib_2/isolate/message3_test.dart b/tests/lib_2/isolate/message3_test.dart
index aa3166c..6536b36 100644
--- a/tests/lib_2/isolate/message3_test.dart
+++ b/tests/lib_2/isolate/message3_test.dart
@@ -60,7 +60,11 @@
}
class E {
+ // Make sure E.fun is not removed by the tree shaker, as this test verifies
+ // that an object with a tear-off in E.fun cannot be sent.
+ @pragma("vm:entry-point")
Function fun;
+
E(this.fun);
static fooFun() => 499;
diff --git a/tools/FAKE_COMMITS b/tools/FAKE_COMMITS
index 597f3da..decbb70 100644
--- a/tools/FAKE_COMMITS
+++ b/tools/FAKE_COMMITS
@@ -30,3 +30,4 @@
Trigger bots
Switch benchmark builders to Ubuntu 16.04
Land a CL with no tryjobs, to validate a fix in copying approvals
+Confirm all approvals took effect
diff --git a/tools/VERSION b/tools/VERSION
index ab560b2..8909bf9 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -33,7 +33,7 @@
MAJOR 2
MINOR 9
PATCH 0
-PRERELEASE 0
-PRERELEASE_PATCH dev
+PRERELEASE 1
+PRERELEASE_PATCH 0
ABI_VERSION 32
OLDEST_SUPPORTED_ABI_VERSION 32
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index ec2f704..e4602d8 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -630,6 +630,7 @@
"--libraries-spec=sdk_nnbd/lib/libraries.json",
"--platform-binaries=out/ReleaseX64NNBD/"
],
+ "timeout": 240,
"host-checked": true
}
},
@@ -644,6 +645,7 @@
"--libraries-spec=sdk_nnbd/lib/libraries.json",
"--platform-binaries=xcodebuild/ReleaseX64NNBD/"
],
+ "timeout": 240,
"host-checked": true
}
},
@@ -658,6 +660,7 @@
"--libraries-spec=sdk_nnbd/lib/libraries.json",
"--platform-binaries=out/ReleaseX64NNBD/"
],
+ "timeout": 240,
"host-checked": true
}
},
@@ -672,6 +675,7 @@
"--libraries-spec=sdk_nnbd/lib/libraries.json",
"--platform-binaries=xcodebuild/ReleaseX64NNBD/"
],
+ "timeout": 240,
"host-checked": true
}
},
@@ -1655,7 +1659,6 @@
"arguments": [
"-ndartkp-${sanitizer}-${system}-${mode}-${arch}",
"vm",
- "standalone",
"standalone_2"
]
}
@@ -3692,4 +3695,4 @@
"linux": "buildtools/linux-x64/clang/bin/llvm-symbolizer",
"macos": "buildtools/mac-x64/clang/bin/llvm-symbolizer"
}
-}
\ No newline at end of file
+}