Version 2.15.0-46.0.dev
Merge commit 'cccc9f93b259dfd6d8f437da30f2a893afa33f78' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 944c834..f6e9780 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,15 @@
This brings the implementation behavior in line with the spec.
+### Tools
+
+#### Dart command line
+
+- **Breaking Change** [#46100][]: The standalone `dart2native` tool has been
+ removed as previously announced. Its replacements are the
+ `dart compile exe` and `dart compile aot-snapshot` commands, which offer the
+ same functionality.
+
## 2.14.0
### Language
diff --git a/DEPS b/DEPS
index 1e12e6a..3a092fd 100644
--- a/DEPS
+++ b/DEPS
@@ -105,7 +105,7 @@
# For more details, see https://github.com/dart-lang/sdk/issues/30164
"dart_style_rev": "14d9b6fd58cc4744676c12be3cc5eee2a779db82",
- "dartdoc_rev" : "5f39ec674d81f5c199151d823fa4ecd01fc59eb2",
+ "dartdoc_rev" : "a4ca86f9bf732d7adc4506f7373e0ed63251b646",
"devtools_rev" : "2b47d9ed486479153ca2fd038000950674ed1beb",
"jsshell_tag": "version:88.0",
"ffi_rev": "4dd32429880a57b64edaf54c9d5af8a9fa9a4ffb",
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 c5d1271..e0ef0b5 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -317,7 +317,7 @@
/// used to create correction producers. The generators are then used to build
/// fixes for those diagnostics. The generators used for non-lint diagnostics
/// are in the [nonLintProducerMap].
- static const Map<String, List<ProducerGenerator>> lintProducerMap = {
+ static final Map<String, List<ProducerGenerator>> lintProducerMap = {
LintNames.always_declare_return_types: [
// TODO(brianwilkerson) Consider applying in bulk.
AddReturnType.newInstance,
@@ -1331,6 +1331,12 @@
}
}
}
+
+ /// Associate the given correction producer [generator] with the lint with the
+ /// given [lintName].
+ static void registerFixForLint(String lintName, ProducerGenerator generator) {
+ lintProducerMap.putIfAbsent(lintName, () => []).add(generator);
+ }
}
/// [_FixState] that is still empty.
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart
index cde9037..6e27913 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor_map_test.dart
@@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -22,6 +23,17 @@
_testMap(FixProcessor.nonLintProducerMap.values);
}
+ void test_registerFixForLint() {
+ CorrectionProducer producer() => MockCorrectionProducer();
+
+ var lintName = 'not_a_lint';
+ expect(FixProcessor.lintProducerMap[lintName], null);
+ FixProcessor.registerFixForLint(lintName, producer);
+ expect(FixProcessor.lintProducerMap[lintName], contains(producer));
+ // Restore the map to it's original state so as to not impact other tests.
+ FixProcessor.lintProducerMap.remove(lintName);
+ }
+
void _testGenerator(ProducerGenerator generator) {
var producer = generator();
var className = producer.runtimeType.toString();
@@ -40,3 +52,10 @@
}
}
}
+
+class MockCorrectionProducer implements CorrectionProducer {
+ @override
+ dynamic noSuchMethod(Invocation invocation) {
+ return super.noSuchMethod(invocation);
+ }
+}
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 46638b1..49d514d 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -58,6 +58,32 @@
import 'relevance_table_generator.dart';
import 'visitors.dart';
+/// Completion metrics are computed by taking a single package and iterating
+/// over the compilation units in the package. For each unit we visit the AST
+/// structure to find all of the places where completion suggestions should be
+/// offered (essentially, everywhere that there's a keyword or identifier). At
+/// each location we compute the completion suggestions using the same code path
+/// used by the analysis server. We then compare the suggestions against the
+/// token that was actually at that location in the file.
+///
+/// This approach has several drawbacks:
+///
+/// - The AST is always complete and correct, and that's rarely the case for
+/// real completion requests. Usually the tree is incomplete and often has a
+/// completely different structure because of the way recovery works. We
+/// currently have no way of measuring completions under realistic conditions.
+///
+/// - We can't measure completions for several keywords because the presence of
+/// the keyword in the AST causes it to not be suggested.
+///
+/// - The time it takes to compute the suggestions doesn't include the time
+/// required to finish analyzing the file if the analysis hasn't been
+/// completed before suggestions are requested. While the times are accurate
+/// (within the accuracy of the `Stopwatch` class) they are the minimum
+/// possible time. This doesn't give us a measure of how completion will
+/// perform in production, but does give us an optimistic approximation.
+///
+/// The first is arguably the worst of the limitations of our current approach.
Future<void> main(List<String> args) async {
var parser = createArgParser();
var result = parser.parse(args);
@@ -1318,7 +1344,7 @@
var filePath = result.path;
// Use the ExpectedCompletionsVisitor to compute the set of expected
// completions for this CompilationUnit.
- final visitor = ExpectedCompletionsVisitor(filePath);
+ final visitor = ExpectedCompletionsVisitor(result);
_resolvedUnitResult.unit.accept(visitor);
for (var expectedCompletion in visitor.expectedCompletions) {
diff --git a/pkg/analysis_server/tool/code_completion/visitors.dart b/pkg/analysis_server/tool/code_completion/visitors.dart
index 5a17c5e..5783758 100644
--- a/pkg/analysis_server/tool/code_completion/visitors.dart
+++ b/pkg/analysis_server/tool/code_completion/visitors.dart
@@ -5,11 +5,13 @@
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analysis_server/src/protocol_server.dart' as protocol;
import 'package:analysis_server/src/services/completion/dart/keyword_contributor.dart';
+import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart' as element;
+import 'package:analyzer/source/line_info.dart';
class ExpectedCompletion {
final String _filePath;
@@ -122,11 +124,12 @@
}
class ExpectedCompletionsVisitor extends RecursiveAstVisitor<void> {
- final List<ExpectedCompletion> expectedCompletions;
+ /// The result of resolving the file being visited.
+ final ResolvedUnitResult result;
- final String filePath;
-
- late CompilationUnit _enclosingCompilationUnit;
+ /// The completions that are expected to be produced in the file being
+ /// visited.
+ final List<ExpectedCompletion> expectedCompletions = [];
/// This boolean is set to enable whether or not we should assert that some
/// found keyword in Dart syntax should be in the completion set returned from
@@ -141,22 +144,22 @@
/// comment don't yield an error like Dart syntax mistakes would yield.
final bool _doExpectCommentRefs = false;
- ExpectedCompletionsVisitor(this.filePath)
- : expectedCompletions = <ExpectedCompletion>[];
+ ExpectedCompletionsVisitor(this.result);
+
+ /// Return the path of the file that is being visited.
+ String get filePath => result.path;
+
+ /// Return the line info for the file that is being visited.
+ LineInfo get lineInfo => result.lineInfo;
void safelyRecordEntity(SyntacticEntity? entity,
{protocol.CompletionSuggestionKind? kind,
protocol.ElementKind? elementKind}) {
// Only record if this entity is not null, has a length, etc.
- if (entity != null && entity.offset > 0 && entity.length > 0) {
- // Compute the line number at this offset
- var lineNumber = _enclosingCompilationUnit.lineInfo!
- .getLocation(entity.offset)
- .lineNumber;
-
- var columnNumber = _enclosingCompilationUnit.lineInfo!
- .getLocation(entity.offset)
- .columnNumber;
+ if (entity != null && entity.offset >= 0 && entity.length > 0) {
+ var location = lineInfo.getLocation(entity.offset);
+ var lineNumber = location.lineNumber;
+ var columnNumber = location.columnNumber;
bool isKeyword() => kind == protocol.CompletionSuggestionKind.KEYWORD;
@@ -315,12 +318,6 @@
}
@override
- void visitCompilationUnit(CompilationUnit node) {
- _enclosingCompilationUnit = node;
- super.visitCompilationUnit(node);
- }
-
- @override
void visitConfiguration(Configuration node) {
safelyRecordKeywordCompletion(node.ifKeyword);
super.visitConfiguration(node);
diff --git a/pkg/analyzer/lib/dart/ast/ast_factory.dart b/pkg/analyzer/lib/dart/ast/ast_factory.dart
index 15022fc..d8469f2 100644
--- a/pkg/analyzer/lib/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/dart/ast/ast_factory.dart
@@ -323,9 +323,22 @@
/// Returns a newly created function body consisting of an expression.
/// The [keyword] can be `null` if the function body is not an async function
/// body.
+ @Deprecated("Use expressionFunctionBody2, with new 'star' parameter")
ExpressionFunctionBody expressionFunctionBody(Token? keyword,
Token functionDefinition, Expression expression, Token? semicolon);
+ /// Returns a newly created function body consisting of an expression.
+ /// The [keyword] can be `null` if the function body is not an async function
+ /// body. The [star] can be `null` if there is no star following the keyword
+ /// (and must be `null` if there is no keyword).
+ ExpressionFunctionBody expressionFunctionBody2({
+ Token? keyword,
+ Token? star,
+ required Token functionDefinition,
+ required Expression expression,
+ Token? semicolon,
+ });
+
/// Returns a newly created expression statement.
ExpressionStatement expressionStatement(
Expression expression, Token? semicolon);
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index a568624..b99b124 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -430,9 +430,8 @@
unignorableCodes.contains(code.name.toUpperCase())) {
return false;
}
-
int errorLine = lineInfo.getLocation(error.offset).lineNumber;
- String name = code.name.toLowerCase();
+ String name = code.name;
if (ignoreInfo.ignoredAt(name, errorLine)) {
return true;
}
@@ -441,8 +440,7 @@
if (period >= 0) {
uniqueName = uniqueName.substring(period + 1);
}
- return uniqueName != name &&
- ignoreInfo.ignoredAt(uniqueName.toLowerCase(), errorLine);
+ return uniqueName != name && ignoreInfo.ignoredAt(uniqueName, errorLine);
}
return errors.where((AnalysisError e) => !isIgnored(e)).toList();
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 434b963..0ea9f43 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -3381,6 +3381,14 @@
@override
Token? keyword;
+ /// The star optionally following the 'async' or 'sync' keyword, or `null` if
+ /// there is wither no such keyword or no star.
+ ///
+ /// It is an error for an expression function body to feature the star, but
+ /// the parser will accept it.
+ @override
+ Token? star;
+
/// The token introducing the expression that represents the body of the
/// function.
@override
@@ -3396,8 +3404,8 @@
/// Initialize a newly created function body consisting of a block of
/// statements. The [keyword] can be `null` if the function body is not an
/// async function body.
- ExpressionFunctionBodyImpl(
- this.keyword, this.functionDefinition, this._expression, this.semicolon) {
+ ExpressionFunctionBodyImpl(this.keyword, this.star, this.functionDefinition,
+ this._expression, this.semicolon) {
_becomeParentOf(_expression);
}
@@ -3412,6 +3420,7 @@
@override
Iterable<SyntacticEntity> get childEntities => ChildEntities()
..add(keyword)
+ ..add(star)
..add(functionDefinition)
..add(_expression)
..add(semicolon);
@@ -3432,10 +3441,13 @@
}
@override
- bool get isAsynchronous => keyword != null;
+ bool get isAsynchronous => keyword?.lexeme == Keyword.ASYNC.lexeme;
@override
- bool get isSynchronous => keyword == null;
+ bool get isGenerator => star != null;
+
+ @override
+ bool get isSynchronous => keyword?.lexeme != Keyword.ASYNC.lexeme;
@override
E? accept<E>(AstVisitor<E> visitor) =>
diff --git a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
index cc2e492..bc7287e 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
@@ -413,8 +413,19 @@
@override
ExpressionFunctionBodyImpl expressionFunctionBody(Token? keyword,
Token functionDefinition, Expression expression, Token? semicolon) =>
- ExpressionFunctionBodyImpl(
- keyword, functionDefinition, expression as ExpressionImpl, semicolon);
+ ExpressionFunctionBodyImpl(keyword, null, functionDefinition,
+ expression as ExpressionImpl, semicolon);
+
+ @override
+ ExpressionFunctionBodyImpl expressionFunctionBody2({
+ Token? keyword,
+ Token? star,
+ required Token functionDefinition,
+ required Expression expression,
+ Token? semicolon,
+ }) =>
+ ExpressionFunctionBodyImpl(keyword, star, functionDefinition,
+ expression as ExpressionImpl, semicolon);
@override
ExpressionStatementImpl expressionStatement(
diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
index d8eaf00..b5929ae 100644
--- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
@@ -333,6 +333,9 @@
var keyword = node.keyword;
if (keyword != null) {
sink.write(keyword.lexeme);
+ if (node.star != null) {
+ sink.write('*');
+ }
sink.write(' ');
}
sink.write('${node.functionDefinition.lexeme} ');
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index a723a79..70109d0 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -413,8 +413,7 @@
bool isIgnored(AnalysisError error) {
int errorLine = lineInfo.getLocation(error.offset).lineNumber;
- String errorCode = error.errorCode.name.toLowerCase();
- return ignoreInfo.ignoredAt(errorCode, errorLine);
+ return ignoreInfo.ignoredAt(error.errorCode.name, errorLine);
}
return errors.where((AnalysisError e) => !isIgnored(e)).toList();
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 873ca31..44ea8bb 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -2665,11 +2665,16 @@
debugEvent("ExpressionFunctionBody");
var expression = pop() as Expression;
- pop(); // star (*)
+ var star = pop() as Token?;
var asyncKeyword = pop() as Token?;
if (parseFunctionBodies) {
- push(ast.expressionFunctionBody(
- asyncKeyword, arrowToken, expression, semicolon));
+ push(ast.expressionFunctionBody2(
+ keyword: asyncKeyword,
+ star: star,
+ functionDefinition: arrowToken,
+ expression: expression,
+ semicolon: semicolon,
+ ));
} else {
push(ast.emptyFunctionBody(semicolon!));
}
diff --git a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
index 70d878c..71aab4d 100644
--- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
@@ -93,11 +93,14 @@
static ExpressionFunctionBodyImpl asyncExpressionFunctionBody(
Expression expression) =>
- astFactory.expressionFunctionBody(
- TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
- TokenFactory.tokenFromType(TokenType.FUNCTION),
- expression,
- TokenFactory.tokenFromType(TokenType.SEMICOLON));
+ astFactory.expressionFunctionBody2(
+ keyword:
+ TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
+ star: null,
+ functionDefinition: TokenFactory.tokenFromType(TokenType.FUNCTION),
+ expression: expression,
+ semicolon: TokenFactory.tokenFromType(TokenType.SEMICOLON),
+ );
static BlockFunctionBodyImpl asyncGeneratorBlockFunctionBody(
[List<Statement> statements = const []]) =>
@@ -106,6 +109,17 @@
TokenFactory.tokenFromType(TokenType.STAR),
block(statements));
+ static ExpressionFunctionBodyImpl asyncGeneratorExpressionFunctionBody(
+ Expression expression) =>
+ astFactory.expressionFunctionBody2(
+ keyword:
+ TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
+ star: TokenFactory.tokenFromType(TokenType.STAR),
+ functionDefinition: TokenFactory.tokenFromType(TokenType.FUNCTION),
+ expression: expression,
+ semicolon: TokenFactory.tokenFromType(TokenType.SEMICOLON),
+ );
+
static AwaitExpressionImpl awaitExpression(Expression expression) =>
astFactory.awaitExpression(
TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "await"),
@@ -485,11 +499,13 @@
static ExpressionFunctionBodyImpl expressionFunctionBody(
Expression expression) =>
- astFactory.expressionFunctionBody(
- null,
- TokenFactory.tokenFromType(TokenType.FUNCTION),
- expression,
- TokenFactory.tokenFromType(TokenType.SEMICOLON));
+ astFactory.expressionFunctionBody2(
+ keyword: null,
+ star: null,
+ functionDefinition: TokenFactory.tokenFromType(TokenType.FUNCTION),
+ expression: expression,
+ semicolon: TokenFactory.tokenFromType(TokenType.SEMICOLON),
+ );
static ExpressionStatementImpl expressionStatement(Expression expression) =>
astFactory.expressionStatement(
diff --git a/pkg/analyzer/lib/src/ignore_comments/ignore_info.dart b/pkg/analyzer/lib/src/ignore_comments/ignore_info.dart
index 8f7b0b3..b2e5b12 100644
--- a/pkg/analyzer/lib/src/ignore_comments/ignore_info.dart
+++ b/pkg/analyzer/lib/src/ignore_comments/ignore_info.dart
@@ -4,7 +4,6 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/dart/ast/token.dart';
/// The name and location of a diagnostic name in an ignore comment.
@@ -26,27 +25,16 @@
/// Information about analysis `//ignore:` and `//ignore_for_file` comments
/// within a source file.
class IgnoreInfo {
- /// Instance shared by all cases without matches.
- // ignore: deprecated_member_use_from_same_package
- static final IgnoreInfo _EMPTY_INFO = IgnoreInfo();
-
- /// A regular expression for matching 'ignore' comments. Produces matches
- /// containing 2 groups. For example:
- ///
- /// * ['//ignore: error_code', 'error_code']
+ /// A regular expression for matching 'ignore' comments.
///
/// Resulting codes may be in a list ('error_code_1,error_code2').
- static final RegExp IGNORE_MATCHER =
- RegExp(r'//+[ ]*ignore:(.*)$', multiLine: true);
+ static final RegExp IGNORE_MATCHER = RegExp(r'//+[ ]*ignore:');
- /// A regular expression for matching 'ignore_for_file' comments. Produces
- /// matches containing 2 groups. For example:
- ///
- /// * ['//ignore_for_file: error_code', 'error_code']
+ /// A regular expression for matching 'ignore_for_file' comments.
///
/// Resulting codes may be in a list ('error_code_1,error_code2').
static final RegExp IGNORE_FOR_FILE_MATCHER =
- RegExp(r'//[ ]*ignore_for_file:(.*)$', multiLine: true);
+ RegExp(r'//[ ]*ignore_for_file:');
/// A table mapping line numbers to the diagnostics that are ignored on that
/// line.
@@ -56,9 +44,6 @@
/// file.
final List<DiagnosticName> _ignoredForFile = [];
- @Deprecated('Use the constructor IgnoreInfo.forDart')
- IgnoreInfo();
-
/// Initialize a newly created instance of this class to represent the ignore
/// comments in the given compilation [unit].
IgnoreInfo.forDart(CompilationUnit unit, String content) {
@@ -68,11 +53,9 @@
if (lexeme.contains('ignore:')) {
var location = lineInfo.getLocation(comment.offset);
var lineNumber = location.lineNumber;
- String beforeMatch = content.substring(
- lineInfo.getOffsetOfLine(lineNumber - 1),
- lineInfo.getOffsetOfLine(lineNumber - 1) +
- location.columnNumber -
- 1);
+ var offsetOfLine = lineInfo.getOffsetOfLine(lineNumber - 1);
+ var beforeMatch = content.substring(
+ offsetOfLine, offsetOfLine + location.columnNumber - 1);
if (beforeMatch.trim().isEmpty) {
// The comment is on its own line, so it refers to the next line.
lineNumber++;
@@ -104,78 +87,21 @@
return ignoredOnLine;
}
- /// Return `true` if the [errorCode] is ignored at the given [line].
+ /// Return `true` if the [errorCode] (case-insensitive) is ignored at the
+ /// given [line].
bool ignoredAt(String errorCode, int line) {
- for (var name in _ignoredForFile) {
- if (name.matches(errorCode)) {
- return true;
- }
+ var ignoredDiagnostics = _ignoredOnLine[line];
+ if (ignoredForFile.isEmpty && ignoredDiagnostics == null) {
+ return false;
}
- var ignoredOnLine = _ignoredOnLine[line];
- if (ignoredOnLine != null) {
- for (var name in ignoredOnLine) {
- if (name.matches(errorCode)) {
- return true;
- }
- }
+ errorCode = errorCode.toLowerCase();
+ if (ignoredForFile.any((name) => name.matches(errorCode))) {
+ return true;
}
- return false;
- }
-
- /// Ignore these [errorCodes] at [line].
- void _addAll(int line, Iterable<DiagnosticName> errorCodes) {
- _ignoredOnLine.putIfAbsent(line, () => []).addAll(errorCodes);
- }
-
- /// Ignore these [errorCodes] in the whole file.
- void _addAllForFile(Iterable<DiagnosticName> errorCodes) {
- _ignoredForFile.addAll(errorCodes);
- }
-
- /// Calculate ignores for the given [content] with line [info].
- @Deprecated('Use the constructor IgnoreInfo.forDart')
- static IgnoreInfo calculateIgnores(String content, LineInfo info) {
- Iterable<Match> matches = IGNORE_MATCHER.allMatches(content);
- Iterable<Match> fileMatches = IGNORE_FOR_FILE_MATCHER.allMatches(content);
- if (matches.isEmpty && fileMatches.isEmpty) {
- return _EMPTY_INFO;
+ if (ignoredDiagnostics == null) {
+ return false;
}
-
- IgnoreInfo ignoreInfo = IgnoreInfo();
- for (Match match in matches) {
- // See _IGNORE_MATCHER for format --- note the possibility of error lists.
- // Note that the offsets are not being computed here. This shouldn't
- // affect older clients of this class because none of the previous APIs
- // depended on having offsets.
- Iterable<DiagnosticName> codes = match
- .group(1)!
- .split(',')
- .map((String code) => DiagnosticName(code.trim().toLowerCase(), -1));
- var location = info.getLocation(match.start);
- int lineNumber = location.lineNumber;
- String beforeMatch = content.substring(
- info.getOffsetOfLine(lineNumber - 1),
- info.getOffsetOfLine(lineNumber - 1) + location.columnNumber - 1);
-
- if (beforeMatch.trim().isEmpty) {
- // The comment is on its own line, so it refers to the next line.
- ignoreInfo._addAll(lineNumber + 1, codes);
- } else {
- // The comment sits next to code, so it refers to its own line.
- ignoreInfo._addAll(lineNumber, codes);
- }
- }
- // Note that the offsets are not being computed here. This shouldn't affect
- // older clients of this class because none of the previous APIs depended on
- // having offsets.
- for (Match match in fileMatches) {
- Iterable<DiagnosticName> codes = match
- .group(1)!
- .split(',')
- .map((String code) => DiagnosticName(code.trim().toLowerCase(), -1));
- ignoreInfo._addAllForFile(codes);
- }
- return ignoreInfo;
+ return ignoredDiagnostics.any((name) => name.matches(errorCode));
}
}
@@ -186,14 +112,10 @@
var comment = currentToken.precedingComments;
while (comment != null) {
var lexeme = comment.lexeme;
- var match = IgnoreInfo.IGNORE_MATCHER.matchAsPrefix(lexeme);
- if (match != null) {
+ if (lexeme.startsWith(IgnoreInfo.IGNORE_MATCHER)) {
yield comment;
- } else {
- match = IgnoreInfo.IGNORE_FOR_FILE_MATCHER.matchAsPrefix(lexeme);
- if (match != null) {
- yield comment;
- }
+ } else if (lexeme.startsWith(IgnoreInfo.IGNORE_FOR_FILE_MATCHER)) {
+ yield comment;
}
comment = comment.next as CommentToken?;
}
diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
index 6119dbf..88e81b9 100644
--- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
+++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
@@ -826,6 +826,13 @@
AstTestFactory.identifier3("a")));
}
+ void test_visitExpressionFunctionBody_async_star() {
+ _assertSource(
+ "async* => a;",
+ AstTestFactory.asyncGeneratorExpressionFunctionBody(
+ AstTestFactory.identifier3("a")));
+ }
+
void test_visitExpressionFunctionBody_simple() {
_assertSource("=> a;",
AstTestFactory.expressionFunctionBody(AstTestFactory.identifier3("a")));
diff --git a/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart b/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart
index 1656f94..d22ab61 100644
--- a/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/illegal_sync_generator_return_type_test.dart
@@ -15,6 +15,14 @@
@reflectiveTest
class IllegalSyncGeneratorReturnTypeTest extends PubPackageResolutionTest {
+ test_arrowFunction_iterator() async {
+ await assertErrorsInCode('''
+Iterable<void> f() sync* => [];
+''', [
+ error(CompileTimeErrorCode.RETURN_IN_GENERATOR, 25, 2),
+ ]);
+ }
+
test_function_iterator() async {
await assertNoErrorsInCode('''
Iterable<void> f() sync* {}
diff --git a/pkg/dart2native/bin/dart2native.dart b/pkg/dart2native/bin/dart2native.dart
deleted file mode 100644
index f8e95ef..0000000
--- a/pkg/dart2native/bin/dart2native.dart
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/usr/bin/env dart
-// 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.
-
-import 'dart:io';
-
-import 'package:args/args.dart';
-import 'package:dart2native/generate.dart';
-import 'package:front_end/src/api_prototype/compiler_options.dart'
- show Verbosity;
-
-void printUsage(final ArgParser parser) {
- print('''
-Usage: dart2native <main-dart-file> [<options>]
-
-Generates an executable or an AOT snapshot from <main-dart-file>.
-''');
- print(parser.usage);
-}
-
-Future<void> main(List<String> args) async {
- // If we're outputting to a terminal, wrap usage text to that width.
- int? outputLineWidth;
- try {
- outputLineWidth = stdout.terminalColumns;
- } catch (_) {/* Ignore. */}
-
- final ArgParser parser = ArgParser(usageLineLength: outputLineWidth)
- ..addMultiOption('define', abbr: 'D', valueHelp: 'key=value', help: '''
-Define an environment declaration. To specify multiple declarations, use multiple options or use commas to separate key-value pairs.
-E.g.: dart2native -Da=1,b=2 main.dart''')
- ..addFlag('enable-asserts',
- negatable: false, help: 'Enable assert statements.')
- ..addMultiOption(
- 'extra-gen-snapshot-options',
- help: 'Pass additional options to gen_snapshot.',
- hide: true,
- valueHelp: 'opt1,opt2,...',
- )
- ..addFlag('help',
- abbr: 'h', negatable: false, help: 'Display this help message.')
- ..addOption(
- 'output-kind',
- abbr: 'k',
- allowed: ['aot', 'exe'],
- allowedHelp: {
- 'aot': 'Generate an AOT snapshot.',
- 'exe': 'Generate a standalone executable.',
- },
- defaultsTo: 'exe',
- valueHelp: 'aot|exe',
- )
- ..addOption('output', abbr: 'o', valueHelp: 'path', help: '''
-Set the output filename. <path> can be relative or absolute.
-E.g.: dart2native main.dart -o ../bin/my_app.exe
-''')
- ..addOption('packages', abbr: 'p', valueHelp: 'path', help: '''
-Get package locations from the specified file instead of .packages. <path> can be relative or absolute.
-E.g.: dart2native --packages=/tmp/pkgs main.dart
-''')
- ..addOption('save-debugging-info', abbr: 'S', valueHelp: 'path', help: '''
-Remove debugging information from the output and save it separately to the specified file. <path> can be relative or absolute.
-''')
- ..addOption('enable-experiment',
- defaultsTo: '', valueHelp: 'feature', hide: true, help: '''
-Comma separated list of experimental features.
-''')
- ..addFlag('sound-null-safety',
- help: 'Respect the nullability of types at runtime.', defaultsTo: null)
- ..addFlag('verbose',
- abbr: 'v', negatable: false, help: 'Show verbose output.')
- ..addOption(
- 'verbosity',
- defaultsTo: Verbosity.defaultValue,
- help: '''
-Sets the verbosity level used for filtering messages during compilation.
-''',
- allowed: Verbosity.allowedValues,
- allowedHelp: Verbosity.allowedValuesHelp,
- );
-
- ArgResults parsedArgs;
- try {
- parsedArgs = parser.parse(args);
- } on FormatException catch (e) {
- stderr.writeln('Error: ${e.message}');
- await stderr.flush();
- printUsage(parser);
- exit(1);
- }
-
- if (parsedArgs['help']) {
- printUsage(parser);
- exit(0);
- }
-
- if (parsedArgs.rest.length != 1) {
- printUsage(parser);
- exit(1);
- }
-
- final String sourceFile = parsedArgs.rest[0];
- if (!FileSystemEntity.isFileSync(sourceFile)) {
- stderr.writeln(
- '"$sourceFile" is not a file. See \'--help\' for more information.');
- await stderr.flush();
- exit(1);
- }
-
- try {
- await generateNative(
- kind: parsedArgs['output-kind'],
- sourceFile: sourceFile,
- outputFile: parsedArgs['output'],
- debugFile: parsedArgs['save-debugging-info'],
- packages: parsedArgs['packages'],
- defines: parsedArgs['define'],
- enableExperiment: parsedArgs['enable-experiment'],
- enableAsserts: parsedArgs['enable-asserts'],
- soundNullSafety: parsedArgs['sound-null-safety'],
- verbose: parsedArgs['verbose'],
- verbosity: parsedArgs['verbosity'],
- extraOptions: parsedArgs['extra-gen-snapshot-options']);
- } catch (e) {
- stderr.writeln('Failed to generate native files:');
- stderr.writeln(e);
- await stderr.flush();
- exit(1);
- }
-}
diff --git a/pkg/dart2native/pubspec.yaml b/pkg/dart2native/pubspec.yaml
index 439a0cb..6cd6814 100644
--- a/pkg/dart2native/pubspec.yaml
+++ b/pkg/dart2native/pubspec.yaml
@@ -10,9 +10,6 @@
dart2native:
dependencies:
- args: ^1.4.0
- front_end:
- path: ../front_end
path: any
dev_dependencies:
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart
index 5027f18..2dc8b55 100644
--- a/pkg/dartdev/lib/src/commands/compile.dart
+++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -239,7 +239,13 @@
defaultsTo: null)
..addOption('save-debugging-info', abbr: 'S', valueHelp: 'path', help: '''
Remove debugging information from the output and save it separately to the specified file.
-<path> can be relative or absolute.''');
+<path> can be relative or absolute.''')
+ ..addMultiOption(
+ 'extra-gen-snapshot-options',
+ help: 'Pass additional options to gen_snapshot.',
+ hide: true,
+ valueHelp: 'opt1,opt2,...',
+ );
addExperimentalFlags(argParser, verbose);
}
@@ -277,6 +283,7 @@
debugFile: argResults['save-debugging-info'],
verbose: verbose,
verbosity: argResults['verbosity'],
+ extraOptions: argResults['extra-gen-snapshot-options'],
);
return 0;
} catch (e) {
diff --git a/pkg/native_stack_traces/README.md b/pkg/native_stack_traces/README.md
index a052851..4e609d5 100644
--- a/pkg/native_stack_traces/README.md
+++ b/pkg/native_stack_traces/README.md
@@ -32,7 +32,7 @@
```dart
@pragma('vm:prefer-inline')
-bar() => throw null;
+bar() => throw Null;
@pragma('vm:never-inline')
foo() => bar();
@@ -44,24 +44,18 @@
```bash
# Make sure that we have the native_stack_traces package.
-$ pub get native_stack_traces
-$ pub global activate native_stack_traces
+$ dart pub global activate native_stack_traces
# We compile the example program, removing the source location information
# from the snapshot and saving the debugging information into throws.debug.
-$ dart2native -k aot -S throws.debug -o throws.aotsnapshot throws.dart
+$ dart compile exe -S throws.debug throws.dart
# Run the program, saving the error output to throws.err.
-$ dartaotruntime throws.aotsnapshot 2>throws.err
+$ ./throws.exe 2>throws.err
# Using the saved debugging information, we can translate the stack trace
# contained in throws.err to its symbolic form.
-$ pub global run native_stack_traces:decode translate -d throws.debug -i throws.err
-
-# We can also just pipe the output of running the program directly into
-# the utility.
-$ dartaotruntime throws.aotsnapshot |& \
- pub global run native_stack_traces:decode translate -d throws.debug
+$ dart pub global run native_stack_traces:decode translate -d throws.debug -i throws.err
```
## Features and bugs
diff --git a/pkg/vm_snapshot_analysis/test/utils.dart b/pkg/vm_snapshot_analysis/test/utils.dart
index 1163ae7..02e25bd 100644
--- a/pkg/vm_snapshot_analysis/test/utils.dart
+++ b/pkg/vm_snapshot_analysis/test/utils.dart
@@ -8,16 +8,15 @@
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
-final dart2native = () {
+final dartCompile = () {
final sdkBin = path.dirname(Platform.executable);
- final dart2native =
- path.join(sdkBin, Platform.isWindows ? 'dart2native.bat' : 'dart2native');
+ final dartCmd = path.join(sdkBin, Platform.isWindows ? 'dart.bat' : 'dart');
- if (!File(dart2native).existsSync()) {
- throw 'Failed to locate dart2native in the SDK';
+ if (!File(dartCmd).existsSync()) {
+ throw 'Failed to locate `dart` in the SDK';
}
- return path.canonicalize(dart2native);
+ return path.canonicalize(dartCmd);
}();
class AotSnapshot {
@@ -61,6 +60,8 @@
];
final args = [
+ 'compile',
+ 'exe',
'-o',
snapshot.outputBinary,
'--packages=$packages',
@@ -69,12 +70,12 @@
];
// Compile input.dart to native and output instruction sizes.
- final result = await Process.run(dart2native, args);
+ final result = await Process.run(dartCompile, args);
expect(result.exitCode, equals(0), reason: '''
Compilation completed with exit code ${result.exitCode}.
-Command line: $dart2native ${args.join(' ')}
+Command line: $dartCompile ${args.join(' ')}
stdout: ${result.stdout}
stderr: ${result.stderr}
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index 9f60f2a..61bef4b 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -931,6 +931,16 @@
return cobject;
}
+Dart_CObject* CObject::NewNativePointer(intptr_t ptr,
+ intptr_t size,
+ Dart_HandleFinalizer callback) {
+ Dart_CObject* cobject = New(Dart_CObject_kNativePointer);
+ cobject->value.as_native_pointer.ptr = ptr;
+ cobject->value.as_native_pointer.size = size;
+ cobject->value.as_native_pointer.callback = callback;
+ return cobject;
+}
+
Dart_CObject* CObject::NewIOBuffer(int64_t length) {
// Make sure that we do not have an integer overflow here. Actual check
// against max elements will be done at the time of writing, as the constant
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index 7ff1580..4eadf084 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -351,6 +351,9 @@
uint8_t* data,
void* peer,
Dart_HandleFinalizer callback);
+ static Dart_CObject* NewNativePointer(intptr_t ptr,
+ intptr_t size,
+ Dart_HandleFinalizer callback);
static Dart_CObject* NewIOBuffer(int64_t length);
static void ShrinkIOBuffer(Dart_CObject* cobject, int64_t new_length);
@@ -579,6 +582,20 @@
DISALLOW_COPY_AND_ASSIGN(CObjectExternalUint8Array);
};
+class CObjectNativePointer : public CObject {
+ public:
+ DECLARE_COBJECT_CONSTRUCTORS(NativePointer)
+
+ intptr_t Ptr() const { return cobject_->value.as_native_pointer.ptr; }
+ intptr_t Size() const { return cobject_->value.as_native_pointer.size; }
+ Dart_HandleFinalizer Callback() const {
+ return cobject_->value.as_native_pointer.callback;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CObjectNativePointer);
+};
+
class ScopedBlockingCall {
public:
ScopedBlockingCall() { Dart_ThreadDisableProfiling(); }
diff --git a/runtime/bin/file.cc b/runtime/bin/file.cc
index 8e377a1..fad12cf 100644
--- a/runtime/bin/file.cc
+++ b/runtime/bin/file.cc
@@ -923,8 +923,8 @@
if (file == NULL) {
return CObject::NewOSError();
}
- return new CObjectIntptr(
- CObject::NewIntptr(reinterpret_cast<intptr_t>(file)));
+ return new CObjectNativePointer(CObject::NewNativePointer(
+ reinterpret_cast<intptr_t>(file), sizeof(*file), ReleaseFile));
}
CObject* File::DeleteRequest(const CObjectArray& request) {
diff --git a/runtime/include/dart_native_api.h b/runtime/include/dart_native_api.h
index ac183b8..f99fff1 100644
--- a/runtime/include/dart_native_api.h
+++ b/runtime/include/dart_native_api.h
@@ -45,6 +45,7 @@
Dart_CObject_kExternalTypedData,
Dart_CObject_kSendPort,
Dart_CObject_kCapability,
+ Dart_CObject_kNativePointer,
Dart_CObject_kUnsupported,
Dart_CObject_kNumberOfTypes
} Dart_CObject_Type;
@@ -80,6 +81,11 @@
void* peer;
Dart_HandleFinalizer callback;
} as_external_typed_data;
+ struct {
+ intptr_t ptr;
+ intptr_t size;
+ Dart_HandleFinalizer callback;
+ } as_native_pointer;
} value;
} Dart_CObject;
// This struct is versioned by DART_API_DL_MAJOR_VERSION, bump the version when
diff --git a/runtime/tests/vm/dart/regress_46796_test.dart b/runtime/tests/vm/dart/regress_46796_test.dart
new file mode 100644
index 0000000..f28f740
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_46796_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// VMOptions=
+// VMOptions=--use_slow_path
+
+class RawSocketEvent {}
+
+class Stream<T> {}
+
+class _RawSocket extends Stream<RawSocketEvent> {
+ int field1 = 512;
+ int field2 = -512;
+
+ _RawSocket() {
+ blackhole(_onSubscriptionStateChange);
+ }
+
+ void _onSubscriptionStateChange<T>() {
+ print("blah");
+ }
+}
+
+@pragma("vm:never-inline")
+blackhole(x) {}
+
+main() {
+ for (var i = 0; i < 10000000; i++) {
+ blackhole(new _RawSocket());
+ }
+}
diff --git a/runtime/tests/vm/dart_2/regress_46796_test.dart b/runtime/tests/vm/dart_2/regress_46796_test.dart
new file mode 100644
index 0000000..5c11b5b
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_46796_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// VMOptions=
+// VMOptions=--use_slow_path
+
+// @dart = 2.9
+
+class RawSocketEvent {}
+
+class Stream<T> {}
+
+class _RawSocket extends Stream<RawSocketEvent> {
+ int field1 = 512;
+ int field2 = -512;
+
+ _RawSocket() {
+ blackhole(_onSubscriptionStateChange);
+ }
+
+ void _onSubscriptionStateChange<T>() {
+ print("blah");
+ }
+}
+
+@pragma("vm:never-inline")
+blackhole(x) {}
+
+main() {
+ for (var i = 0; i < 10000000; i++) {
+ blackhole(new _RawSocket());
+ }
+}
diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h
index 5ca709a..e7f7cbc 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -203,6 +203,10 @@
// Illegal class id.
kIllegalCid = 0,
+ // Pseudo class id for native pointers, the heap should never see an
+ // object with this class id.
+ kNativePointer,
+
// The following entries describes classes for pseudo-objects in the heap
// that should never be reachable from live objects. Free list elements
// maintain the free list for old space, and forwarding corpses are used to
@@ -281,10 +285,11 @@
// and should not be exposed directly to user code.
inline bool IsInternalOnlyClassId(intptr_t index) {
// Fix the condition below if these become non-contiguous.
- COMPILE_ASSERT(kIllegalCid + 1 == kFreeListElement &&
- kIllegalCid + 2 == kForwardingCorpse &&
- kIllegalCid + 3 == kObjectCid &&
- kIllegalCid + 4 == kFirstInternalOnlyCid);
+ COMPILE_ASSERT(kIllegalCid + 1 == kNativePointer &&
+ kIllegalCid + 2 == kFreeListElement &&
+ kIllegalCid + 3 == kForwardingCorpse &&
+ kIllegalCid + 4 == kObjectCid &&
+ kIllegalCid + 5 == kFirstInternalOnlyCid);
return index <= kLastInternalOnlyCid;
}
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 7a139c4..11775fc 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -243,16 +243,18 @@
// Allocate context.
{
Label done, slow_path;
- __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1),
- &slow_path,
- R0, // instance
- R1, // end address
- R2, R3);
- __ ldr(R1, Address(THR, target::Thread::object_null_offset()));
- __ str(R1, FieldAddress(R0, target::Context::parent_offset()));
- __ LoadImmediate(R1, 1);
- __ str(R1, FieldAddress(R0, target::Context::num_variables_offset()));
- __ b(&done);
+ if (!FLAG_use_slow_path && FLAG_inline_alloc) {
+ __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1),
+ &slow_path,
+ R0, // instance
+ R1, // end address
+ R2, R3);
+ __ ldr(R1, Address(THR, target::Thread::object_null_offset()));
+ __ str(R1, FieldAddress(R0, target::Context::parent_offset()));
+ __ LoadImmediate(R1, 1);
+ __ str(R1, FieldAddress(R0, target::Context::num_variables_offset()));
+ __ b(&done);
+ }
__ Bind(&slow_path);
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 2c1288d..916604b 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -472,7 +472,7 @@
__ cmp(R4, Operand(0));
__ b(&no_type_args, EQ);
__ ldr(R0, Address(FP, kReceiverOffset * target::kWordSize));
- __ ldr(R3, Address(R0, R4));
+ __ LoadCompressed(R3, Address(R0, R4));
__ Bind(&no_type_args);
// Push type arguments & extracted method.
@@ -482,16 +482,18 @@
// Allocate context.
{
Label done, slow_path;
- __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1),
- &slow_path,
- R0, // instance
- R1, // end address
- R2, R3);
- __ ldr(R1, Address(THR, target::Thread::object_null_offset()));
- __ str(R1, FieldAddress(R0, target::Context::parent_offset()));
- __ LoadImmediate(R1, 1);
- __ str(R1, FieldAddress(R0, target::Context::num_variables_offset()));
- __ b(&done);
+ if (!FLAG_use_slow_path && FLAG_inline_alloc) {
+ __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1),
+ &slow_path,
+ R0, // instance
+ R1, // end address
+ R2, R3);
+ __ ldr(R1, Address(THR, target::Thread::object_null_offset()));
+ __ str(R1, FieldAddress(R0, target::Context::parent_offset()));
+ __ LoadImmediate(R1, 1);
+ __ str(R1, FieldAddress(R0, target::Context::num_variables_offset()));
+ __ b(&done);
+ }
__ Bind(&slow_path);
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 74405e0..74bab68 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -417,7 +417,7 @@
__ cmpq(RDX, Immediate(0));
__ j(EQUAL, &no_type_args, Assembler::kNearJump);
__ movq(RAX, Address(RBP, target::kWordSize * kReceiverOffsetInWords));
- __ movq(RCX, Address(RAX, RDX, TIMES_1, 0));
+ __ LoadCompressed(RCX, Address(RAX, RDX, TIMES_1, 0));
__ Bind(&no_type_args);
__ pushq(RCX);
@@ -427,16 +427,18 @@
// Allocate context.
{
Label done, slow_path;
- __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1),
- &slow_path, Assembler::kFarJump,
- RAX, // instance
- RSI, // end address
- RDI);
- __ movq(RSI, Address(THR, target::Thread::object_null_offset()));
- __ movq(FieldAddress(RAX, target::Context::parent_offset()), RSI);
- __ movq(FieldAddress(RAX, target::Context::num_variables_offset()),
- Immediate(1));
- __ jmp(&done);
+ if (!FLAG_use_slow_path && FLAG_inline_alloc) {
+ __ TryAllocateArray(kContextCid, target::Context::InstanceSize(1),
+ &slow_path, Assembler::kFarJump,
+ RAX, // instance
+ RSI, // end address
+ RDI);
+ __ movq(RSI, Address(THR, target::Thread::object_null_offset()));
+ __ movq(FieldAddress(RAX, target::Context::parent_offset()), RSI);
+ __ movq(FieldAddress(RAX, target::Context::num_variables_offset()),
+ Immediate(1));
+ __ jmp(&done);
+ }
__ Bind(&slow_path);
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index e65fc1c..ef3fba1 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -1818,6 +1818,67 @@
const intptr_t cid_;
};
+class NativePointerMessageSerializationCluster
+ : public MessageSerializationCluster {
+ public:
+ explicit NativePointerMessageSerializationCluster(Zone* zone)
+ : MessageSerializationCluster("NativePointer",
+ MessagePhase::kNonCanonicalInstances,
+ kNativePointer),
+ objects_(zone, 0) {}
+ ~NativePointerMessageSerializationCluster() {}
+
+ void Trace(MessageSerializer* s, Object* object) { UNREACHABLE(); }
+
+ void WriteNodes(MessageSerializer* s) { UNREACHABLE(); }
+
+ void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) {
+ objects_.Add(object);
+ }
+
+ void WriteNodesApi(ApiMessageSerializer* s) {
+ intptr_t count = objects_.length();
+ s->WriteUnsigned(count);
+ for (intptr_t i = 0; i < count; i++) {
+ Dart_CObject* data = objects_[i];
+ s->AssignRef(data);
+
+ intptr_t ptr = data->value.as_native_pointer.ptr;
+ s->WriteUnsigned(ptr);
+
+ s->finalizable_data()->Put(
+ data->value.as_native_pointer.size, nullptr,
+ reinterpret_cast<void*>(data->value.as_native_pointer.ptr),
+ data->value.as_native_pointer.callback);
+ }
+ }
+
+ private:
+ GrowableArray<Dart_CObject*> objects_;
+};
+
+class NativePointerMessageDeserializationCluster
+ : public MessageDeserializationCluster {
+ public:
+ NativePointerMessageDeserializationCluster()
+ : MessageDeserializationCluster("NativePointer"), cid_(kNativePointer) {}
+ ~NativePointerMessageDeserializationCluster() {}
+
+ void ReadNodes(MessageDeserializer* d) {
+ intptr_t count = d->ReadUnsigned();
+ for (intptr_t i = 0; i < count; i++) {
+ intptr_t ptr = d->ReadUnsigned();
+ d->finalizable_data()->Take();
+ d->AssignRef(Integer::New(ptr));
+ }
+ }
+
+ void ReadNodesApi(ApiMessageDeserializer* d) { UNREACHABLE(); }
+
+ private:
+ const intptr_t cid_;
+};
+
class TypedDataViewMessageSerializationCluster
: public MessageSerializationCluster {
public:
@@ -3189,6 +3250,9 @@
case Dart_CObject_kCapability:
cid = kCapabilityCid;
break;
+ case Dart_CObject_kNativePointer:
+ cid = kNativePointer;
+ break;
default:
return Fail("invalid Dart_CObject type");
}
@@ -3242,6 +3306,8 @@
}
switch (cid) {
+ case kNativePointer:
+ return new (Z) NativePointerMessageSerializationCluster(Z);
case kClassCid:
return new (Z) ClassMessageSerializationCluster();
case kTypeArgumentsCid:
@@ -3326,6 +3392,9 @@
}
switch (cid) {
+ case kNativePointer:
+ ASSERT(!is_canonical);
+ return new (Z) NativePointerMessageDeserializationCluster();
case kClassCid:
ASSERT(!is_canonical);
return new (Z) ClassMessageDeserializationCluster();
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index 7067a89..654967a 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -37,7 +37,6 @@
# ......dartaotruntime or dartaotruntime.exe (executable)
# ......dartdoc
# ......dartfmt
-# ......dart2native (if not on ia32)
# ......dart2js
# ......dartanalyzer
# ......dartdevc
@@ -46,7 +45,6 @@
# ......snapshots/
# ........analysis_server.dart.snapshot
# ........dart2js.dart.snapshot
-# ........dart2native.dart.snapshot (if not on ia32)
# ........dartanalyzer.dart.snapshot
# ........dartdev.dart.snapshot
# ........dartdev.dill
@@ -147,12 +145,6 @@
"../utils/pub",
],
]
-if (dart_target_arch != "ia32") {
- _platform_sdk_snapshots += [ [
- "dart2native",
- "../utils/dart2native:generate_dart2native_snapshot",
- ] ]
-}
if (create_kernel_service_snapshot) {
_platform_sdk_snapshots += [ [
"kernel-service",
@@ -270,8 +262,8 @@
{
target = "copy_${library}_library"
visibility = [
- ":copy_platform_sdk_libraries",
":copy_full_sdk_libraries",
+ ":copy_platform_sdk_libraries",
]
source = "lib/$library"
dest = "$root_out_dir/$dart_sdk_output/lib/$library"
@@ -328,7 +320,7 @@
}
copy("copy_dartaotruntime") {
- visibility = [ ":copy_dart2native" ]
+ visibility = [ ":group_dart2native" ]
deps = [ "../runtime/bin:dart_precompiled_runtime_product" ]
src_dir = get_label_info("../runtime/bin:dart_precompiled_runtime_product",
"root_out_dir")
@@ -341,7 +333,7 @@
}
copy("copy_gen_snapshot") {
- visibility = [ ":copy_dart2native" ]
+ visibility = [ ":group_dart2native" ]
deps = [ "../runtime/bin:gen_snapshot_product" ]
src_dir =
get_label_info("../runtime/bin:gen_snapshot_product", "root_out_dir")
@@ -352,7 +344,7 @@
}
copy("copy_vm_platform_strong_product") {
- visibility = [ ":copy_dart2native" ]
+ visibility = [ ":group_dart2native" ]
deps = [ "../runtime/vm:vm_platform_product" ]
src_dir = get_label_info("../runtime/vm:vm_platform_product", "root_out_dir")
sources = [ "$src_dir/vm_platform_strong_product.dill" ]
@@ -360,27 +352,21 @@
[ "$root_out_dir/$dart_sdk_output/lib/_internal/{{source_file_part}}" ]
}
-copy("copy_dart2native") {
+copy("copy_gen_kernel_snapshot") {
+ visibility = [ ":group_dart2native" ]
+ deps = [ "../utils/gen_kernel" ]
+ sources = [ "$root_gen_dir/gen_kernel.dart.snapshot" ]
+ outputs =
+ [ "$root_out_dir/$dart_sdk_output/bin/snapshots/{{source_file_part}}" ]
+}
+
+group("group_dart2native") {
deps = [
":copy_dartaotruntime",
":copy_gen_kernel_snapshot",
":copy_gen_snapshot",
":copy_vm_platform_strong_product",
]
- ext = ""
- if (is_win) {
- ext = ".bat"
- }
- sources = [ "bin/dart2native$ext" ]
- outputs = [ "$root_out_dir/$dart_sdk_output/bin/{{source_file_part}}" ]
-}
-
-copy("copy_gen_kernel_snapshot") {
- visibility = [ ":copy_dart2native" ]
- deps = [ "../utils/gen_kernel" ]
- sources = [ "$root_gen_dir/gen_kernel.dart.snapshot" ]
- outputs =
- [ "$root_out_dir/$dart_sdk_output/bin/snapshots/{{source_file_part}}" ]
}
# A template for copying the things in _platform_sdk_scripts and
@@ -394,8 +380,8 @@
}
copy(target_name) {
visibility = [
- ":copy_platform_sdk_scripts",
":copy_full_sdk_scripts",
+ ":copy_platform_sdk_scripts",
]
sources = [ "bin/${name}_sdk$ext" ]
outputs = [ "$root_out_dir/$dart_sdk_output/bin/$name$ext" ]
@@ -411,8 +397,8 @@
foreach(script, _scripts) {
copy("copy_${script}_script") {
visibility = [
- ":copy_platform_sdk_scripts",
":copy_full_sdk_scripts",
+ ":copy_platform_sdk_scripts",
]
ext = ""
if (is_win) {
@@ -458,8 +444,8 @@
}
copy("copy_${snapshot[0]}_snapshot") {
visibility = [
- ":copy_platform_sdk_snapshots",
":copy_full_sdk_snapshots",
+ ":copy_platform_sdk_snapshots",
]
deps = [ snapshot[1] ]
sources = [ "$root/${snapshot[0]}.dart.snapshot" ]
@@ -695,8 +681,8 @@
# This is the main rule to copy libraries in _full_sdk_libraries to lib/
group("copy_full_sdk_libraries") {
visibility = [
- ":create_full_sdk",
":copy_libraries",
+ ":create_full_sdk",
]
public_deps = []
foreach(library, _full_sdk_libraries) {
@@ -796,8 +782,8 @@
# Parts common to both platform and full SDKs.
group("create_common_sdk") {
visibility = [
- ":create_sdk",
":create_platform_sdk",
+ ":create_sdk",
]
public_deps = [
":copy_analysis_summaries",
@@ -818,17 +804,17 @@
]
# We do not support AOT on ia32 and should therefore not add the
- # dart2native script (since there is no AOT compiler/runtime available)
+ # dart native compilation files (since there is no AOT compiler/runtime available)
if (dart_target_arch != "ia32") {
- public_deps += [ ":copy_dart2native" ]
+ public_deps += [ ":group_dart2native" ]
}
}
# Parts specific to the platform SDK.
group("_create_platform_sdk") {
visibility = [
- ":create_sdk",
":create_platform_sdk",
+ ":create_sdk",
]
public_deps = [
":copy_platform_sdk_libraries",
diff --git a/sdk/bin/dart2native b/sdk/bin/dart2native
deleted file mode 100755
index 6f59f3f..0000000
--- a/sdk/bin/dart2native
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-# 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.
-
-# Run dart2native.dart.snapshot on the Dart VM
-
-echo "Warning: 'dart2native' is deprecated. Please use 'dart compile exe'." 1>&2
-
-function follow_links() {
- file="$1"
- while [ -h "$file" ]; do
- # On Mac OS, readlink -f doesn't work.
- file="$(readlink "$file")"
- done
- echo "$file"
-}
-
-# Unlike $0, $BASH_SOURCE points to the absolute path of this file.
-PROG_NAME="$(follow_links "$BASH_SOURCE")"
-
-# Handle the case where dart-sdk/bin has been symlinked to.
-BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-SNAPSHOTS_DIR="${BIN_DIR}/snapshots"
-DART="$BIN_DIR/dart"
-
-exec "$DART" "${SNAPSHOTS_DIR}/dart2native.dart.snapshot" "$@"
diff --git a/sdk/bin/dart2native.bat b/sdk/bin/dart2native.bat
deleted file mode 100644
index ca4d8d7..0000000
--- a/sdk/bin/dart2native.bat
+++ /dev/null
@@ -1,45 +0,0 @@
-@echo off
-REM Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-REM for details. All rights reserved. Use of this source code is governed by a
-REM BSD-style license that can be found in the LICENSE file.
-
-echo Warning: 'dart2native' is deprecated. Please use 'dart compile exe'. 1>&2
-
-setlocal
-rem Handle the case where dart-sdk/bin has been symlinked to.
-set DIR_NAME_WITH_SLASH=%~dp0
-set DIR_NAME=%DIR_NAME_WITH_SLASH:~0,-1%%
-call :follow_links "%DIR_NAME%", RETURNED_BIN_DIR
-rem Get rid of surrounding quotes.
-for %%i in ("%RETURNED_BIN_DIR%") do set BIN_DIR=%%~fi
-
-set DART=%BIN_DIR%\dart
-
-"%DART%" "%BIN_DIR%\snapshots\dart2native.dart.snapshot" %*
-
-endlocal
-
-exit /b %errorlevel%
-
-rem Follow the symbolic links (junctions points) using `dir to determine the
-rem canonical path. Output with a link looks something like this
-rem
-rem 01/03/2013 10:11 PM <JUNCTION> abc def
-rem [c:\dart_bleeding\dart-repo.9\dart\out\ReleaseIA32\dart-sdk]
-rem
-rem So in the output of 'dir /a:l "targetdir"' we are looking for a filename
-rem surrounded by right angle bracket and left square bracket. Once we get
-rem the filename, which is name of the link, we recursively follow that.
-:follow_links
-setlocal
-for %%i in (%1) do set result=%%~fi
-set current=
-for /f "usebackq tokens=2 delims=[]" %%i in (`dir /a:l "%~dp1" 2^>nul ^
- ^| %SystemRoot%\System32\find.exe "> %~n1 [" 2^>nul`) do (
- set current=%%i
-)
-if not "%current%"=="" call :follow_links "%current%", result
-endlocal & set %~2=%result%
-goto :eof
-
-:end
diff --git a/tests/standalone/io/file_leak_test.dart b/tests/standalone/io/file_leak_test.dart
new file mode 100644
index 0000000..6c8ddf1
--- /dev/null
+++ b/tests/standalone/io/file_leak_test.dart
@@ -0,0 +1,139 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Dart test program for testing file I/O.
+
+// OtherResources=fixed_length_file
+// OtherResources=read_as_text.dat
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:collection';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+import "package:path/path.dart";
+
+class FileTest {
+ static Directory? tempDirectory;
+ static int numLiveAsyncTests = 0;
+
+ static void asyncTestStarted() {
+ asyncStart();
+ ++numLiveAsyncTests;
+ }
+
+ static void asyncTestDone(String name) {
+ asyncEnd();
+ --numLiveAsyncTests;
+ if (numLiveAsyncTests == 0) {
+ deleteTempDirectory();
+ }
+ }
+
+ static void createTempDirectory(Function doNext) {
+ Directory.systemTemp.createTemp('dart_file').then((temp) {
+ tempDirectory = temp;
+ doNext();
+ });
+ }
+
+ static void deleteTempDirectory() {
+ tempDirectory!.deleteSync(recursive: true);
+ }
+
+ static testReadInto() async {
+ asyncTestStarted();
+ File file = new File(tempDirectory!.path + "/out_read_into");
+
+ var openedFile = await file.open(mode: FileMode.write);
+ await openedFile.writeFrom(const [1, 2, 3]);
+
+ await openedFile.setPosition(0);
+ var list = <int>[1, 2, 3];
+ Expect.equals(3, await openedFile.readInto(list));
+ Expect.listEquals([1, 2, 3], list);
+
+ read(start, end, length, expected) async {
+ var list = <int>[1, 2, 3];
+ await openedFile.setPosition(0);
+ Expect.equals(length, await openedFile.readInto(list, start, end));
+ Expect.listEquals(expected, list);
+ return list;
+ }
+
+ await read(0, 3, 3, [1, 2, 3]);
+ await read(0, 2, 2, [1, 2, null]);
+ await read(1, 2, 1, [null, 1, null]);
+ await read(1, 3, 2, [null, 1, 2]);
+ await read(2, 3, 1, [null, null, 1]);
+ await read(0, 0, 0, [null, null, null]);
+
+ await openedFile.close();
+
+ asyncTestDone("testReadInto");
+ }
+
+ static void testReadAsText() {
+ asyncTestStarted();
+ var name = getFilename("fixed_length_file");
+ var f = new File(name);
+ f.readAsString(encoding: utf8).then((text) {
+ Expect.isTrue(text.endsWith("42 bytes."));
+ Expect.equals(42, text.length);
+ var name = getFilename("read_as_text.dat");
+ var f = new File(name);
+ f.readAsString(encoding: utf8).then((text) {
+ Expect.equals(6, text.length);
+ var expected = [955, 120, 46, 32, 120, 10];
+ Expect.listEquals(expected, text.codeUnits);
+ f.readAsString(encoding: latin1).then((text) {
+ Expect.equals(7, text.length);
+ var expected = [206, 187, 120, 46, 32, 120, 10];
+ Expect.listEquals(expected, text.codeUnits);
+ var readAsStringFuture = f.readAsString(encoding: ascii);
+ readAsStringFuture.then((text) {
+ Expect.fail("Non-ascii char should cause error");
+ }).catchError((e) {
+ asyncTestDone("testReadAsText");
+ });
+ });
+ });
+ });
+ }
+
+ static String getFilename(String path) {
+ return Platform.script.resolve(path).toFilePath();
+ }
+
+ // Main test entrypoint.
+ // This test results in an unhandled exception in the isolate while
+ // some async file IO operations are still pending. The unhandled
+ // exception results in the 'File' object being leaked, the error
+ // only shows up in the ASAN bots which detect the leak.
+ static testMain() {
+ asyncStart();
+ var outerZone = Zone.current;
+ var firstZone = Zone.current.fork(specification: ZoneSpecification(
+ handleUncaughtError: (self, parent, zone, error, stacktrace) {
+ asyncEnd();
+ print("unittest-suite-success"); // For the test harness.
+ exit(0);
+ }));
+ firstZone.run(() async {
+ Expect.identical(firstZone, Zone.current);
+ createTempDirectory(() {
+ testReadAsText();
+ testReadInto();
+ Expect.equals(1, 0); // Should not execute this.
+ asyncEnd();
+ });
+ });
+ }
+}
+
+main() {
+ FileTest.testMain();
+}
diff --git a/tests/standalone_2/io/file_leak_test.dart b/tests/standalone_2/io/file_leak_test.dart
new file mode 100644
index 0000000..9bac244
--- /dev/null
+++ b/tests/standalone_2/io/file_leak_test.dart
@@ -0,0 +1,141 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Dart test program for testing file I/O.
+
+// @dart = 2.9
+
+// OtherResources=fixed_length_file
+// OtherResources=read_as_text.dat
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:collection';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+import "package:path/path.dart";
+
+class FileTest {
+ static Directory tempDirectory;
+ static int numLiveAsyncTests = 0;
+
+ static void asyncTestStarted() {
+ asyncStart();
+ ++numLiveAsyncTests;
+ }
+
+ static void asyncTestDone(String name) {
+ asyncEnd();
+ --numLiveAsyncTests;
+ if (numLiveAsyncTests == 0) {
+ deleteTempDirectory();
+ }
+ }
+
+ static void createTempDirectory(Function doNext) {
+ Directory.systemTemp.createTemp('dart_file').then((temp) {
+ tempDirectory = temp;
+ doNext();
+ });
+ }
+
+ static void deleteTempDirectory() {
+ tempDirectory.deleteSync(recursive: true);
+ }
+
+ static testReadInto() async {
+ asyncTestStarted();
+ File file = new File(tempDirectory.path + "/out_read_into");
+
+ var openedFile = await file.open(mode: FileMode.write);
+ await openedFile.writeFrom(const [1, 2, 3]);
+
+ await openedFile.setPosition(0);
+ var list = [null, null, null];
+ Expect.equals(3, await openedFile.readInto(list));
+ Expect.listEquals([1, 2, 3], list);
+
+ read(start, end, length, expected) async {
+ var list = [null, null, null];
+ await openedFile.setPosition(0);
+ Expect.equals(length, await openedFile.readInto(list, start, end));
+ Expect.listEquals(expected, list);
+ return list;
+ }
+
+ await read(0, 3, 3, [1, 2, 3]);
+ await read(0, 2, 2, [1, 2, null]);
+ await read(1, 2, 1, [null, 1, null]);
+ await read(1, 3, 2, [null, 1, 2]);
+ await read(2, 3, 1, [null, null, 1]);
+ await read(0, 0, 0, [null, null, null]);
+
+ await openedFile.close();
+
+ asyncTestDone("testReadInto");
+ }
+
+ static void testReadAsText() {
+ asyncTestStarted();
+ var name = getFilename("fixed_length_file");
+ var f = new File(name);
+ f.readAsString(encoding: utf8).then((text) {
+ Expect.isTrue(text.endsWith("42 bytes."));
+ Expect.equals(42, text.length);
+ var name = getFilename("read_as_text.dat");
+ var f = new File(name);
+ f.readAsString(encoding: utf8).then((text) {
+ Expect.equals(6, text.length);
+ var expected = [955, 120, 46, 32, 120, 10];
+ Expect.listEquals(expected, text.codeUnits);
+ f.readAsString(encoding: latin1).then((text) {
+ Expect.equals(7, text.length);
+ var expected = [206, 187, 120, 46, 32, 120, 10];
+ Expect.listEquals(expected, text.codeUnits);
+ var readAsStringFuture = f.readAsString(encoding: ascii);
+ readAsStringFuture.then((text) {
+ Expect.fail("Non-ascii char should cause error");
+ }).catchError((e) {
+ asyncTestDone("testReadAsText");
+ });
+ });
+ });
+ });
+ }
+
+ static String getFilename(String path) {
+ return Platform.script.resolve(path).toFilePath();
+ }
+
+ // Main test entrypoint.
+ // This test results in an unhandled exception in the isolate while
+ // some async file IO operations are still pending. The unhandled
+ // exception results in the 'File' object being leaked, the error
+ // only shows up in the ASAN bots which detect the leak.
+ static testMain() {
+ asyncStart();
+ var outerZone = Zone.current;
+ var firstZone = Zone.current.fork(specification: ZoneSpecification(
+ handleUncaughtError: (self, parent, zone, error, stacktrace) {
+ asyncEnd();
+ print("unittest-suite-success"); // For the test harness.
+ exit(0);
+ }));
+ firstZone.run(() async {
+ Expect.identical(firstZone, Zone.current);
+ createTempDirectory(() {
+ testReadAsText();
+ testReadInto();
+ Expect.equals(1, 0); // Should not execute this.
+ asyncEnd();
+ });
+ });
+ }
+}
+
+main() {
+ FileTest.testMain();
+}
diff --git a/tools/VERSION b/tools/VERSION
index b6717ad..b711534 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 45
+PRERELEASE 46
PRERELEASE_PATCH 0
\ No newline at end of file