Version 1.12.0-dev.2.0
Merge commit '0590f7e630152f5b5265a8101016456c0da71a69' into dev
diff --git a/.gitattributes b/.gitattributes
index f9b1135..d77d09b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -23,7 +23,6 @@
tests/lib/mirrors/method_mirror_source_line_ending_lf.dart -text
tests/lib/mirrors/method_mirror_source_test.dart -text
tests/lib/mirrors/method_mirror_source_other.dart -text
-pkg/js_ast/test/printer_callback_test.dart -text
# Files to leave alone and not diff.
*.png binary
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 884a584..164dd2c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,11 @@
`setInnerHtml` or other methods that create DOM from text. It is
also more efficient, skipping the creation of a `DocumentFragment`.
+* `dart:io`
+ * Added two new file modes, `WRITE_ONLY` and `WRITE_ONLY_APPEND` for
+ opening a file write only.
+ [eaeecf2](https://github.com/dart-lang/sdk/commit/eaeecf2ed13ba6c7fbfd653c3c592974a7120960)
+
### Tool changes
* Pub
@@ -53,7 +58,7 @@
* List iterators may not throw `ConcurrentModificationError` as eagerly in
release mode. In checked mode, the modification check is still as eager
as possible.
- [r45198](https://code.google.com/p/dart/source/detail?r=45198)
+ [r45198](https://github.com/dart-lang/sdk/commit/5a79c03)
* `dart:developer` - **NEW**
* Replaces the deprecated `dart:profiler` library.
@@ -102,9 +107,9 @@
* **POTENTIALLY BREAKING** Fix behavior of `HtmlEscape`. It no longer escapes
no-break space (U+00A0) anywhere or forward slash (`/`, `U+002F`) in element
context. Slash is still escaped using `HtmlEscapeMode.UNKNOWN`.
- [r45003](https://code.google.com/p/dart/source/detail?r=45003),
- [r45153](https://code.google.com/p/dart/source/detail?r=45153),
- [r45189](https://code.google.com/p/dart/source/detail?r=45189)
+ [r45003](https://github.com/dart-lang/sdk/commit/8b8223d),
+ [r45153](https://github.com/dart-lang/sdk/commit/8a5d049),
+ [r45189](https://github.com/dart-lang/sdk/commit/3c39ad2)
* `dart:core`
* `Uri.parse` added `start` and `end` positional arguments.
diff --git a/DEPS b/DEPS
index 321d387..ef0d4fa 100644
--- a/DEPS
+++ b/DEPS
@@ -40,6 +40,7 @@
"7zip_rev" : "@19997",
"analyzer_cli_rev" : "@8bf3516dd645ca289d7ebc641f7c228d5b3d37c4",
"args_tag": "@0.13.0",
+ "async_tag": "@1.2.0",
"barback_rev" : "@29ee90dbcf77cfd64632fa2797a4c8a4f29a4b51",
"charcode_tag": "@1.1.0",
"chrome_rev" : "@19997",
@@ -50,7 +51,7 @@
"csslib_tag" : "@0.12.0",
"dartdoc_rev" : "@9f677ec40f9beeb8933374885ef3af4c63d35d25",
"dart_services_rev" : "@7aea2574e6f3924bf409a80afb8ad52aa2be4f97",
- "dart_style_tag": "@0.1.8",
+ "dart_style_tag": "@0.1.8+1",
"dev_compiler_rev": "@0.1.1",
"fake_async_rev" : "@38614",
"firefox_jsshell_rev" : "@45554",
@@ -82,23 +83,24 @@
"petitparser_rev" : "@37878",
"ply_rev": "@604b32590ffad5cbb82e4afef1d305512d06ae93",
"plugin_tag": "@0.1.0",
- "pool_rev": "@22e12aeb16ad0b626900dbe79e4a25391ddfb28c",
- "pub_rev": "@0c02113cc761ec5f142a8c41ff277505fafa5e10",
+ "pool_rev": "@e454b4b54d2987e8d2f0fbd3ac519641ada9bd0f",
+ "pub_rev": "@e05cfca67574acfdbce55a8422c6bc458be93d10",
"pub_cache_tag": "@v0.0.1+2",
"pub_semver_tag": "@1.2.1",
"quiver_tag": "@0.21.4",
- "scheduled_test_tag": "@0.11.8+1",
+ "scheduled_test_tag": "@0.12.1+2",
"shelf_rev": "@1e87b79b21ac5e6fa2f93576d6c06eaa65285ef4",
"smoke_rev" : "@f3361191cc2a85ebc1e4d4c33aec672d7915aba9",
- "source_maps_rev": "@379b4f31c4e2987eb15934d1ad8b419c6cc897b3",
+ "source_maps_tag": "@0.10.1",
"sqlite_rev": "@38811b79f42801662adc0458a25270ab690a6b81",
"shelf_static_rev": "@v0.2.1",
"shelf_web_socket_rev": "@ff170cec2c0e4e5722cdf47c557be63b5035a602",
- "source_span_rev": "@42501132e43599a151ba6727d340e44442f86c05",
- "stack_trace_tag": "@1.2.1",
+ "source_map_stack_trace_tag": "@1.0.4",
+ "source_span_tag": "@1.1.2",
+ "stack_trace_tag": "@1.3.4",
"string_scanner_rev": "@3e7617d6f74ba382e9b6130b1cc12091d89a9bc5",
"sunflower_rev": "@879b704933413414679396b129f5dfa96f7a0b1e",
- "test_tag": "@0.12.1",
+ "test_tag": "@0.12.3+4",
"test_reflective_loader_tag": "@0.0.3",
"utf_rev": "@1f55027068759e2d52f2c12de6a57cce5f3c5ee6",
"unittest_tag": "@0.11.6",
@@ -180,6 +182,8 @@
(Var("github_mirror") % "analyzer_cli") + Var("analyzer_cli_rev"),
Var("dart_root") + "/third_party/pkg/args":
(Var("github_mirror") % "args") + Var("args_tag"),
+ Var("dart_root") + "/third_party/pkg/async":
+ "https://github.com/dart-lang/async.git" + Var("async_tag"),
Var("dart_root") + "/third_party/pkg/barback":
(Var("github_mirror") % "barback") + Var("barback_rev"),
Var("dart_root") + "/third_party/pkg/charcode":
@@ -270,9 +274,12 @@
Var("dart_root") + "/third_party/pkg/smoke":
(Var("github_mirror") % "smoke") + Var("smoke_rev"),
Var("dart_root") + "/third_party/pkg/source_maps":
- (Var("github_mirror") % "source_maps") + Var("source_maps_rev"),
+ (Var("github_mirror") % "source_maps") + Var("source_maps_tag"),
Var("dart_root") + "/third_party/pkg/source_span":
- (Var("github_mirror") % "source_span") + Var("source_span_rev"),
+ (Var("github_mirror") % "source_span") + Var("source_span_tag"),
+ Var("dart_root") + "/third_party/pkg/source_map_stack_trace":
+ "https://github.com/dart-lang/source_map_stack_trace.git" +
+ Var("source_map_stack_trace_tag"),
Var("dart_root") + "/third_party/pkg/stack_trace":
(Var("github_mirror") % "stack_trace") + Var("stack_trace_tag"),
Var("dart_root") + "/third_party/pkg/string_scanner":
diff --git a/WATCHLISTS b/WATCHLISTS
index 1bbea0d..762e901 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -14,14 +14,18 @@
'filepath': 'tools/',
},
'observatory': {
- 'filepath': 'runtime/observatory/',
+ 'filepath': 'runtime/bin/vmservice/' \
+ '|runtime/bin/vmservice*' \
+ '|runtime/observatory/' \
+ '|runtime/vm/service/' \
+ '|runtime/vm/service*'
},
},
'WATCHLISTS': {
'runtime': ['vm-dev@dartlang.org'],
'tools': ['ricow@google.com'],
- 'observatory': ['johnmccutchan@google.com', 'turnidge@google.com'],
+ 'observatory': ['johnmccutchan@google.com', 'turnidge@google.com', 'rmacnak@google.com'],
},
}
diff --git a/pkg/analysis_server/test/performance/driver.dart b/pkg/analysis_server/benchmark/integration/driver.dart
similarity index 79%
rename from pkg/analysis_server/test/performance/driver.dart
rename to pkg/analysis_server/benchmark/integration/driver.dart
index 4418227..19243ea 100644
--- a/pkg/analysis_server/test/performance/driver.dart
+++ b/pkg/analysis_server/benchmark/integration/driver.dart
@@ -5,12 +5,13 @@
library server.driver;
import 'dart:async';
-import 'dart:math' show max;
+import 'dart:math' show max, sqrt;
+import 'package:analyzer/src/generated/engine.dart' as engine;
import 'package:logging/logging.dart';
-import '../integration/integration_test_methods.dart';
-import '../integration/integration_tests.dart';
+import '../../test/integration/integration_test_methods.dart';
+import '../../test/integration/integration_tests.dart';
import 'operation.dart';
final SPACE = ' '.codeUnitAt(0);
@@ -96,7 +97,7 @@
* Launch the analysis server.
* Return a [Future] that completes when analysis server has started.
*/
- Future startServer() async {
+ Future startServer({int diagnosticPort}) async {
logger.log(Level.FINE, 'starting server');
initializeInttestMixin();
server = new Server();
@@ -106,7 +107,9 @@
serverConnected.complete();
});
running = true;
- return server.start(/*profileServer: true*/).then((params) {
+ return server
+ .start(diagnosticPort: diagnosticPort /*profileServer: true*/)
+ .then((params) {
server.listenToOutput(dispatchNotification);
server.exitCode.then((_) {
logger.log(Level.FINE, 'server stopped');
@@ -169,14 +172,29 @@
minTime = minTime.compareTo(elapsed) < 0 ? minTime : elapsed;
totalTimeMicros += timeMicros;
}
- int averageTimeMicros = (totalTimeMicros / count).round();
+ int meanTime = (totalTimeMicros / count).round();
+ List<Duration> sorted = elapsedTimes.toList()..sort();
+ Duration time90th = sorted[(sorted.length * 0.90).round() - 1];
+ Duration time99th = sorted[(sorted.length * 0.99).round() - 1];
+ int differenceFromMeanSquared = 0;
+ for (Duration elapsed in elapsedTimes) {
+ int timeMicros = elapsed.inMicroseconds;
+ int differenceFromMean = timeMicros - meanTime;
+ differenceFromMeanSquared += differenceFromMean * differenceFromMean;
+ }
+ double variance = differenceFromMeanSquared / count;
+ int standardDeviation = sqrt(variance).round();
+
StringBuffer sb = new StringBuffer();
_printColumn(sb, tag, keyLen);
_printColumn(sb, count.toString(), 6, rightJustified: true);
_printColumn(sb, errorCount.toString(), 6, rightJustified: true);
_printColumn(sb, unexpectedResultCount.toString(), 6, rightJustified: true);
+ _printDuration(sb, new Duration(microseconds: meanTime));
+ _printDuration(sb, time90th);
+ _printDuration(sb, time99th);
+ _printColumn(sb, standardDeviation.toString(), 15, rightJustified: true);
_printDuration(sb, minTime);
- _printDuration(sb, new Duration(microseconds: averageTimeMicros));
_printDuration(sb, maxTime);
_printDuration(sb, new Duration(microseconds: totalTimeMicros));
print(sb.toString());
@@ -213,6 +231,11 @@
void printResults() {
print('');
print('==================================================================');
+ if (engine.AnalysisEngine.instance.useTaskModel) {
+ print('New task model');
+ } else {
+ print('Old task model');
+ }
print('');
List<String> keys = measurements.keys.toList()..sort();
int keyLen = keys.fold(0, (int len, String key) => max(len, key.length));
@@ -229,7 +252,8 @@
totalUnexpectedResultCount += m.unexpectedResultCount;
}
}
- _printTotals(keyLen, totalCount, totalErrorCount, totalUnexpectedResultCount);
+ _printTotals(
+ keyLen, totalCount, totalErrorCount, totalUnexpectedResultCount);
print('');
_printGroupHeader('Notifications', keyLen);
for (String tag in keys) {
@@ -265,19 +289,23 @@
void _printGroupHeader(String groupName, int keyLen) {
StringBuffer sb = new StringBuffer();
- _printColumn(sb, groupName, keyLen);
- _printColumn(sb, 'count', 6, rightJustified: true);
- _printColumn(sb, 'error', 6, rightJustified: true);
- _printColumn(sb, 'uxr(1)', 6, rightJustified: true);
- sb.write(' ');
- _printColumn(sb, 'minimum', 15);
- _printColumn(sb, 'average', 15);
- _printColumn(sb, 'maximum', 15);
- _printColumn(sb, 'total', 15);
- print(sb.toString());
+ _printColumn(sb, groupName, keyLen);
+ _printColumn(sb, 'count', 6, rightJustified: true);
+ _printColumn(sb, 'error', 6, rightJustified: true);
+ _printColumn(sb, 'uxr(1)', 6, rightJustified: true);
+ sb.write(' ');
+ _printColumn(sb, 'mean', 15);
+ _printColumn(sb, '90th', 15);
+ _printColumn(sb, '99th', 15);
+ _printColumn(sb, 'std-dev', 15);
+ _printColumn(sb, 'minimum', 15);
+ _printColumn(sb, 'maximum', 15);
+ _printColumn(sb, 'total', 15);
+ print(sb.toString());
}
- void _printTotals(int keyLen, int totalCount, int totalErrorCount, int totalUnexpectedResultCount) {
+ void _printTotals(int keyLen, int totalCount, int totalErrorCount,
+ int totalUnexpectedResultCount) {
StringBuffer sb = new StringBuffer();
_printColumn(sb, 'Totals', keyLen);
_printColumn(sb, totalCount.toString(), 6, rightJustified: true);
diff --git a/pkg/analysis_server/test/performance/input_converter.dart b/pkg/analysis_server/benchmark/integration/input_converter.dart
similarity index 95%
rename from pkg/analysis_server/test/performance/input_converter.dart
rename to pkg/analysis_server/benchmark/integration/input_converter.dart
index ccf598e..ef90131 100644
--- a/pkg/analysis_server/test/performance/input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/input_converter.dart
@@ -70,7 +70,13 @@
*/
final String tmpSrcDirPath;
- CommonInputConverter(this.tmpSrcDirPath, this.srcPathMap);
+ /**
+ * The diagnostic port for Analysis Server or `null` if none.
+ */
+ final int diagnosticPort;
+
+ CommonInputConverter(this.tmpSrcDirPath, this.srcPathMap,
+ {this.diagnosticPort});
/**
* Return an operation for the notification or `null` if none.
@@ -89,7 +95,7 @@
}
if (event == SERVER_CONNECTED) {
// {"event":"server.connected","params":{"version":"1.7.0"}}
- return new StartServerOperation();
+ return new StartServerOperation(diagnosticPort: diagnosticPort);
}
if (eventsSeen.add(event)) {
logger.log(Level.INFO, 'Ignored notification: $event\n $json');
@@ -284,6 +290,11 @@
final String tmpSrcDirPath;
/**
+ * The diagnostic port for Analysis Server or `null` if none.
+ */
+ final int diagnosticPort;
+
+ /**
* The number of lines read before the underlying converter was determined
* or the end of file was reached.
*/
@@ -301,7 +312,7 @@
*/
bool active = true;
- InputConverter(this.tmpSrcDirPath, this.srcPathMap);
+ InputConverter(this.tmpSrcDirPath, this.srcPathMap, {this.diagnosticPort});
@override
Operation convert(String line) {
@@ -320,9 +331,11 @@
throw 'Failed to determine input file format';
}
if (InstrumentationInputConverter.isFormat(line)) {
- converter = new InstrumentationInputConverter(tmpSrcDirPath, srcPathMap);
+ converter = new InstrumentationInputConverter(tmpSrcDirPath, srcPathMap,
+ diagnosticPort: diagnosticPort);
} else if (LogFileInputConverter.isFormat(line)) {
- converter = new LogFileInputConverter(tmpSrcDirPath, srcPathMap);
+ converter = new LogFileInputConverter(tmpSrcDirPath, srcPathMap,
+ diagnosticPort: diagnosticPort);
}
if (converter != null) {
return converter.convert(line);
diff --git a/pkg/analysis_server/test/performance/instrumentation_input_converter.dart b/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
similarity index 96%
rename from pkg/analysis_server/test/performance/instrumentation_input_converter.dart
rename to pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
index 32974c2..11e2f9a 100644
--- a/pkg/analysis_server/test/performance/instrumentation_input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/instrumentation_input_converter.dart
@@ -30,8 +30,9 @@
StringBuffer readBuffer = null;
InstrumentationInputConverter(
- String tmpSrcDirPath, Map<String, String> srcPathMap)
- : super(tmpSrcDirPath, srcPathMap);
+ String tmpSrcDirPath, Map<String, String> srcPathMap,
+ {int diagnosticPort})
+ : super(tmpSrcDirPath, srcPathMap, diagnosticPort: diagnosticPort);
@override
Operation convert(String line) {
diff --git a/pkg/analysis_server/test/performance/local_runner.dart b/pkg/analysis_server/benchmark/integration/local_runner.dart
similarity index 94%
rename from pkg/analysis_server/test/performance/local_runner.dart
rename to pkg/analysis_server/benchmark/integration/local_runner.dart
index 0cbe6e1..1d8d245 100644
--- a/pkg/analysis_server/test/performance/local_runner.dart
+++ b/pkg/analysis_server/benchmark/integration/local_runner.dart
@@ -62,12 +62,10 @@
*/
performance.main([
//'-vv', // very verbose
- '-i',
- inputFile.path,
- '-t',
- tmpSrcDirPath,
- '-m',
- '${gitDir.path},$tmpSrcDirPath',
+ //'-d8081', // analysis server localhost diagnostic port
+ '-i${inputFile.path}',
+ '-t$tmpSrcDirPath',
+ '-m${gitDir.path},$tmpSrcDirPath',
]);
}
diff --git a/pkg/analysis_server/test/performance/log_file_input_converter.dart b/pkg/analysis_server/benchmark/integration/log_file_input_converter.dart
similarity index 95%
rename from pkg/analysis_server/test/performance/log_file_input_converter.dart
rename to pkg/analysis_server/benchmark/integration/log_file_input_converter.dart
index 6e9e74e..dd6c35c 100644
--- a/pkg/analysis_server/test/performance/log_file_input_converter.dart
+++ b/pkg/analysis_server/benchmark/integration/log_file_input_converter.dart
@@ -23,8 +23,9 @@
* into a series of operations to be sent to the analysis server.
*/
class LogFileInputConverter extends CommonInputConverter {
- LogFileInputConverter(String tmpSrcDirPath, Map<String, String> srcPathMap)
- : super(tmpSrcDirPath, srcPathMap);
+ LogFileInputConverter(String tmpSrcDirPath, Map<String, String> srcPathMap,
+ {int diagnosticPort})
+ : super(tmpSrcDirPath, srcPathMap, diagnosticPort: diagnosticPort);
@override
Operation convert(String line) {
diff --git a/pkg/analysis_server/test/performance/main.dart b/pkg/analysis_server/benchmark/integration/main.dart
similarity index 83%
rename from pkg/analysis_server/test/performance/main.dart
rename to pkg/analysis_server/benchmark/integration/main.dart
index d7ec220..793110d 100644
--- a/pkg/analysis_server/test/performance/main.dart
+++ b/pkg/analysis_server/benchmark/integration/main.dart
@@ -10,18 +10,13 @@
import 'package:args/args.dart';
import 'package:logging/logging.dart';
+import 'package:path/path.dart' as path;
import 'driver.dart';
import 'input_converter.dart';
import 'operation.dart';
/**
- * The amount of time to give the server to respond to a shutdown request
- * before forcibly terminating it.
- */
-const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 25);
-
-/**
* Launch and interact with the analysis server.
*/
main(List<String> rawArgs) {
@@ -57,9 +52,17 @@
});
}
+const DIAGNOSTIC_PORT_OPTION = 'diagnosticPort';
const HELP_CMDLINE_OPTION = 'help';
const INPUT_CMDLINE_OPTION = 'input';
const MAP_OPTION = 'map';
+
+/**
+ * The amount of time to give the server to respond to a shutdown request
+ * before forcibly terminating it.
+ */
+const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 25);
+
const TMP_SRC_DIR_OPTION = 'tmpSrcDir';
const VERBOSE_CMDLINE_OPTION = 'verbose';
const VERY_VERBOSE_CMDLINE_OPTION = 'vv';
@@ -84,7 +87,8 @@
return inputRaw
.transform(SYSTEM_ENCODING.decoder)
.transform(new LineSplitter())
- .transform(new InputConverter(args.tmpSrcDirPath, args.srcPathMap));
+ .transform(new InputConverter(args.tmpSrcDirPath, args.srcPathMap,
+ diagnosticPort: args.diagnosticPort));
}
/**
@@ -109,6 +113,9 @@
parser.addOption(TMP_SRC_DIR_OPTION, abbr: 't', help: '<dirPath>\n'
'The temporary directory containing source used during performance measurement.\n'
'WARNING: The contents of the target directory will be modified');
+ parser.addOption(DIAGNOSTIC_PORT_OPTION,
+ abbr: 'd',
+ help: 'localhost port on which server will provide diagnostic web pages');
parser.addFlag(VERBOSE_CMDLINE_OPTION,
abbr: 'v', help: 'Verbose logging', negatable: false);
parser.addFlag(VERY_VERBOSE_CMDLINE_OPTION,
@@ -141,10 +148,9 @@
if (pair is String) {
int index = pair.indexOf(',');
if (index != -1 && pair.indexOf(',', index + 1) == -1) {
- String oldSrcPath = pair.substring(0, index);
- String newSrcPath = pair.substring(index + 1);
- if (new Directory(oldSrcPath).existsSync() &&
- new Directory(newSrcPath).existsSync()) {
+ String oldSrcPath = _withTrailingSeparator(pair.substring(0, index));
+ String newSrcPath = _withTrailingSeparator(pair.substring(index + 1));
+ if (new Directory(newSrcPath).existsSync()) {
perfArgs.srcPathMap[oldSrcPath] = newSrcPath;
continue;
}
@@ -154,12 +160,20 @@
showHelp = true;
}
- perfArgs.tmpSrcDirPath = args[TMP_SRC_DIR_OPTION];
+ perfArgs.tmpSrcDirPath = _withTrailingSeparator(args[TMP_SRC_DIR_OPTION]);
if (isMissing(TMP_SRC_DIR_OPTION)) {
print('missing $TMP_SRC_DIR_OPTION argument');
showHelp = true;
}
+ String portText = args[DIAGNOSTIC_PORT_OPTION];
+ if (portText != null) {
+ perfArgs.diagnosticPort = int.parse(portText, onError: (s) {
+ print('invalid $DIAGNOSTIC_PORT_OPTION: $s');
+ showHelp = true;
+ });
+ }
+
if (args[VERY_VERBOSE_CMDLINE_OPTION] || rawArgs.contains('-vv')) {
Logger.root.level = Level.FINE;
} else if (args[VERBOSE_CMDLINE_OPTION]) {
@@ -184,12 +198,24 @@
}
/**
+ * Ensure that the given path has a trailing separator
+ */
+String _withTrailingSeparator(String dirPath) {
+ if (dirPath != null && dirPath.length > 4) {
+ if (!dirPath.endsWith(path.separator)) {
+ return '$dirPath${path.separator}';
+ }
+ }
+ return dirPath;
+}
+
+/**
* The performance measurement arguments specified on the command line.
*/
class PerfArgs {
/**
- * The file path of the instrumentation or log file
+ * The file path of the instrumentation or log file
* used to drive performance measurement,
* or 'stdin' if this information should be read from standard input.
*/
@@ -206,4 +232,9 @@
* The temporary directory containing source used during performance measurement.
*/
String tmpSrcDirPath;
+
+ /**
+ * The diagnostic port for Analysis Server or `null` if none.
+ */
+ int diagnosticPort;
}
diff --git a/pkg/analysis_server/test/performance/operation.dart b/pkg/analysis_server/benchmark/integration/operation.dart
similarity index 95%
rename from pkg/analysis_server/test/performance/operation.dart
rename to pkg/analysis_server/benchmark/integration/operation.dart
index 641cf31..f9ee62a 100644
--- a/pkg/analysis_server/test/performance/operation.dart
+++ b/pkg/analysis_server/benchmark/integration/operation.dart
@@ -13,7 +13,7 @@
import 'input_converter.dart';
/**
- * A [CompletionRequestOperation] tracks response time along with
+ * A [CompletionRequestOperation] tracks response time along with
* the first and last completion notifications.
*/
class CompletionRequestOperation extends RequestOperation {
@@ -80,6 +80,7 @@
Stopwatch stopwatch = new Stopwatch();
String originalId = json['id'];
String method = json['method'];
+ json['clientRequestTime'] = new DateTime.now().millisecondsSinceEpoch;
driver.logger.log(Level.FINE, 'Sending request: $method\n $json');
stopwatch.start();
@@ -178,9 +179,13 @@
}
class StartServerOperation extends Operation {
+ final int diagnosticPort;
+
+ StartServerOperation({this.diagnosticPort});
+
@override
Future perform(Driver driver) {
- return driver.startServer();
+ return driver.startServer(diagnosticPort: diagnosticPort);
}
}
@@ -194,7 +199,6 @@
Completer completer = new Completer();
bool isAnalyzing = false;
subscription = driver.onServerStatus.listen((ServerStatusParams params) {
- // TODO (danrubel) ensure that server.setSubscriptions STATUS is set
if (params.analysis != null) {
if (params.analysis.isAnalyzing) {
isAnalyzing = true;
@@ -218,10 +222,10 @@
completer.complete();
return;
}
- // Timeout if no communcation received within the last 10 seconds.
+ // Timeout if no communcation received within the last 60 seconds.
double currentTime = driver.server.currentElapseTime;
double lastTime = driver.server.lastCommunicationTime;
- if (currentTime - lastTime > 10) {
+ if (currentTime - lastTime > 60) {
subscription.cancel();
timer.cancel();
String message = 'gave up waiting for analysis to complete';
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index db15b5f..6d8be8a 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -22,6 +22,7 @@
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.dart';
+import 'package:analysis_server/uri/resolver_provider.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/ast.dart';
@@ -260,13 +261,14 @@
OptimizingPubPackageMapProvider packageMapProvider, Index _index,
this.serverPlugin, AnalysisServerOptions analysisServerOptions,
this.defaultSdk, this.instrumentationService,
- {this.rethrowExceptions: true})
+ {ResolverProvider packageResolverProvider: null,
+ this.rethrowExceptions: true})
: index = _index,
searchEngine = _index != null ? createSearchEngine(_index) : null {
_performance = performanceDuringStartup;
operationQueue = new ServerOperationQueue();
- contextDirectoryManager = new ServerContextManager(
- this, resourceProvider, packageMapProvider, instrumentationService);
+ contextDirectoryManager = new ServerContextManager(this, resourceProvider,
+ packageResolverProvider, packageMapProvider, instrumentationService);
contextDirectoryManager.defaultOptions.incremental = true;
contextDirectoryManager.defaultOptions.incrementalApi =
analysisServerOptions.enableIncrementalResolutionApi;
@@ -1310,9 +1312,11 @@
StreamController<ContextsChangedEvent> _onContextsChangedController;
ServerContextManager(this.analysisServer, ResourceProvider resourceProvider,
+ ResolverProvider packageResolverProvider,
OptimizingPubPackageMapProvider packageMapProvider,
InstrumentationService service)
- : super(resourceProvider, packageMapProvider, service) {
+ : super(resourceProvider, packageResolverProvider, packageMapProvider,
+ service) {
_onContextsChangedController =
new StreamController<ContextsChangedEvent>.broadcast();
}
diff --git a/pkg/analysis_server/lib/src/computer/computer_navigation.dart b/pkg/analysis_server/lib/src/computer/computer_navigation.dart
index 504bf40..14a47ec 100644
--- a/pkg/analysis_server/lib/src/computer/computer_navigation.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_navigation.dart
@@ -246,17 +246,15 @@
}
// add regions
TypeName typeName = node.type;
+ computer._addRegionForNode(typeName.name, element);
+ // <TypeA, TypeB>
TypeArgumentList typeArguments = typeName.typeArguments;
- if (typeArguments == null) {
- computer._addRegion_nodeStart_nodeEnd(parent, node, element);
- } else {
- computer._addRegion_nodeStart_nodeEnd(parent, typeName.name, element);
- // <TypeA, TypeB>
+ if (typeArguments != null) {
typeArguments.accept(this);
- // optional ".name"
- if (node.period != null) {
- computer._addRegion_tokenStart_nodeEnd(node.period, node, element);
- }
+ }
+ // optional "name"
+ if (node.name != null) {
+ computer._addRegionForNode(node.name, element);
}
}
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 275df09..f75b702 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -10,6 +10,7 @@
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.dart';
+import 'package:analysis_server/uri/resolver_provider.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/source/package_map_resolver.dart';
@@ -80,6 +81,13 @@
Map<String, String> normalizedPackageRoots = <String, String>{};
/**
+ * A function that will return a [UriResolver] that can be used to resolve
+ * `package:` URI's within a given folder, or `null` if we should fall back
+ * to the standard URI resolver.
+ */
+ final ResolverProvider packageResolverProvider;
+
+ /**
* Provider which is used to determine the mapping from package name to
* package folder.
*/
@@ -90,8 +98,8 @@
*/
final InstrumentationService _instrumentationService;
- ContextManager(this.resourceProvider, this._packageMapProvider,
- this._instrumentationService) {
+ ContextManager(this.resourceProvider, this.packageResolverProvider,
+ this._packageMapProvider, this._instrumentationService) {
pathContext = resourceProvider.pathContext;
}
@@ -431,6 +439,12 @@
return new PackageUriResolver([packagesDir]);
} else {
beginComputePackageMap();
+ if (packageResolverProvider != null) {
+ UriResolver resolver = packageResolverProvider(folder);
+ if (resolver != null) {
+ return resolver;
+ }
+ }
OptimizingPubPackageMapInfo packageMapInfo;
ServerPerformanceStatistics.pub.makeCurrentWhile(() {
packageMapInfo =
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 0c1f9db9b..7604fc0 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -14,6 +14,7 @@
import 'package:analysis_server/src/server/stdio_server.dart';
import 'package:analysis_server/src/socket_server.dart';
import 'package:analysis_server/starter.dart';
+import 'package:analysis_server/uri/resolver_provider.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/instrumentation/file_instrumentation.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
@@ -283,6 +284,12 @@
InstrumentationServer instrumentationServer;
/**
+ * The package resolver provider used to override the way package URI's are
+ * resolved in some contexts.
+ */
+ ResolverProvider packageResolverProvider;
+
+ /**
* The plugins that are defined outside the analysis_server package.
*/
List<Plugin> _userDefinedPlugins = <Plugin>[];
@@ -388,8 +395,8 @@
//
// Create the sockets and start listening for requests.
//
- socketServer = new SocketServer(
- analysisServerOptions, defaultSdk, service, serverPlugin);
+ socketServer = new SocketServer(analysisServerOptions, defaultSdk, service,
+ serverPlugin, packageResolverProvider);
httpServer = new HttpAnalysisServer(socketServer);
stdioServer = new StdioAnalysisServer(socketServer);
socketServer.userDefinedPlugins = _userDefinedPlugins;
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart
index 8234ed8..5bdbe24 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -57,6 +57,10 @@
const AssistKind('CONVERT_INTO_IS_NOT', 30, "Convert into is!");
static const CONVERT_INTO_IS_NOT_EMPTY = const AssistKind(
'CONVERT_INTO_IS_NOT_EMPTY', 30, "Convert into 'isNotEmpty'");
+ static const CONVERT_TO_FIELD_PARAMETER = const AssistKind(
+ 'CONVERT_TO_FIELD_PARAMETER', 30, "Convert to field formal parameter");
+ static const CONVERT_TO_NORMAL_PARAMETER = const AssistKind(
+ 'CONVERT_TO_NORMAL_PARAMETER', 30, "Convert to normal parameter");
static const ENCAPSULATE_FIELD =
const AssistKind('ENCAPSULATE_FIELD', 30, "Encapsulate field");
static const EXCHANGE_OPERANDS =
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index 5d03c83..9c5e9ba 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -87,6 +87,8 @@
_addProposal_convertToIsNot_onIs();
_addProposal_convertToIsNot_onNot();
_addProposal_convertToIsNotEmpty();
+ _addProposal_convertToFieldParameter();
+ _addProposal_convertToNormalParameter();
_addProposal_encapsulateField();
_addProposal_exchangeOperands();
_addProposal_importAddShow();
@@ -443,6 +445,93 @@
_addAssist(DartAssistKind.CONVERT_INTO_EXPRESSION_BODY, []);
}
+ void _addProposal_convertToFieldParameter() {
+ if (node == null) {
+ return;
+ }
+ // prepare ConstructorDeclaration
+ ConstructorDeclaration constructor =
+ node.getAncestor((node) => node is ConstructorDeclaration);
+ if (constructor == null) {
+ return;
+ }
+ FormalParameterList parameterList = constructor.parameters;
+ List<ConstructorInitializer> initializers = constructor.initializers;
+ // prepare parameter
+ SimpleFormalParameter parameter;
+ if (node.parent is SimpleFormalParameter &&
+ node.parent.parent is FormalParameterList &&
+ node.parent.parent.parent is ConstructorDeclaration) {
+ parameter = node.parent;
+ }
+ if (node is SimpleIdentifier &&
+ node.parent is ConstructorFieldInitializer) {
+ String name = (node as SimpleIdentifier).name;
+ ConstructorFieldInitializer initializer = node.parent;
+ if (initializer.expression == node) {
+ for (FormalParameter formalParameter in parameterList.parameters) {
+ if (formalParameter is SimpleFormalParameter &&
+ formalParameter.identifier.name == name) {
+ parameter = formalParameter;
+ }
+ }
+ }
+ }
+ // analyze parameter
+ if (parameter != null) {
+ String parameterName = parameter.identifier.name;
+ ParameterElement parameterElement = parameter.element;
+ // check number of references
+ {
+ int numOfReferences = 0;
+ AstVisitor visitor = new _SimpleIdentifierRecursiveAstVisitor(
+ (SimpleIdentifier node) {
+ if (node.staticElement == parameterElement) {
+ numOfReferences++;
+ }
+ });
+ for (ConstructorInitializer initializer in initializers) {
+ initializer.accept(visitor);
+ }
+ if (numOfReferences != 1) {
+ return;
+ }
+ }
+ // find the field initializer
+ ConstructorFieldInitializer parameterInitializer;
+ for (ConstructorInitializer initializer in initializers) {
+ if (initializer is ConstructorFieldInitializer) {
+ Expression expression = initializer.expression;
+ if (expression is SimpleIdentifier &&
+ expression.name == parameterName) {
+ parameterInitializer = initializer;
+ }
+ }
+ }
+ if (parameterInitializer == null) {
+ return;
+ }
+ String fieldName = parameterInitializer.fieldName.name;
+ // replace parameter
+ _addReplaceEdit(rangeNode(parameter), 'this.$fieldName');
+ // remove initializer
+ int initializerIndex = initializers.indexOf(parameterInitializer);
+ if (initializers.length == 1) {
+ _addRemoveEdit(rangeEndEnd(parameterList, parameterInitializer));
+ } else {
+ if (initializerIndex == 0) {
+ ConstructorInitializer next = initializers[initializerIndex + 1];
+ _addRemoveEdit(rangeStartStart(parameterInitializer, next));
+ } else {
+ ConstructorInitializer prev = initializers[initializerIndex - 1];
+ _addRemoveEdit(rangeEndEnd(prev, parameterInitializer));
+ }
+ }
+ // add proposal
+ _addAssist(DartAssistKind.CONVERT_TO_FIELD_PARAMETER, []);
+ }
+ }
+
void _addProposal_convertToForIndexLoop() {
// find enclosing ForEachStatement
ForEachStatement forEachStatement =
@@ -673,6 +762,38 @@
_addAssist(DartAssistKind.CONVERT_INTO_IS_NOT_EMPTY, []);
}
+ void _addProposal_convertToNormalParameter() {
+ if (node is SimpleIdentifier &&
+ node.parent is FieldFormalParameter &&
+ node.parent.parent is FormalParameterList &&
+ node.parent.parent.parent is ConstructorDeclaration) {
+ ConstructorDeclaration constructor = node.parent.parent.parent;
+ FormalParameterList parameterList = node.parent.parent;
+ FieldFormalParameter parameter = node.parent;
+ ParameterElement parameterElement = parameter.element;
+ String name = (node as SimpleIdentifier).name;
+ // prepare type
+ DartType type = parameterElement.type;
+ Set<LibraryElement> librariesToImport = new Set<LibraryElement>();
+ String typeCode = utils.getTypeSource(type, librariesToImport);
+ // replace parameter
+ if (type.isDynamic) {
+ _addReplaceEdit(rangeNode(parameter), name);
+ } else {
+ _addReplaceEdit(rangeNode(parameter), '$typeCode $name');
+ }
+ // add field initializer
+ List<ConstructorInitializer> initializers = constructor.initializers;
+ if (initializers.isEmpty) {
+ _addInsertEdit(parameterList.end, ' : $name = $name');
+ } else {
+ _addInsertEdit(initializers.last.end, ', $name = $name');
+ }
+ // add proposal
+ _addAssist(DartAssistKind.CONVERT_TO_NORMAL_PARAMETER, []);
+ }
+ }
+
void _addProposal_encapsulateField() {
// find FieldDeclaration
FieldDeclaration fieldDeclaraton =
diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart
index 5f95186..36e7836 100644
--- a/pkg/analysis_server/lib/src/socket_server.dart
+++ b/pkg/analysis_server/lib/src/socket_server.dart
@@ -11,6 +11,7 @@
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analysis_server/src/services/index/local_file_index.dart';
import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.dart';
+import 'package:analysis_server/uri/resolver_provider.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/sdk_io.dart';
@@ -27,6 +28,7 @@
final DirectoryBasedDartSdk defaultSdk;
final InstrumentationService instrumentationService;
final ServerPlugin serverPlugin;
+ final ResolverProvider packageResolverProvider;
/**
* The analysis server that was created when a client established a
@@ -40,7 +42,8 @@
List<Plugin> userDefinedPlugins;
SocketServer(this.analysisServerOptions, this.defaultSdk,
- this.instrumentationService, this.serverPlugin);
+ this.instrumentationService, this.serverPlugin,
+ this.packageResolverProvider);
/**
* Create an analysis server which will communicate with the client using the
@@ -78,7 +81,9 @@
analysisServer = new AnalysisServer(serverChannel, resourceProvider,
new OptimizingPubPackageMapProvider(resourceProvider, defaultSdk),
index, serverPlugin, analysisServerOptions, defaultSdk,
- instrumentationService, rethrowExceptions: false);
+ instrumentationService,
+ packageResolverProvider: packageResolverProvider,
+ rethrowExceptions: false);
analysisServer.userDefinedPlugins = userDefinedPlugins;
}
}
diff --git a/pkg/analysis_server/lib/src/status/get_handler.dart b/pkg/analysis_server/lib/src/status/get_handler.dart
index 01a330a..3e18f8a 100644
--- a/pkg/analysis_server/lib/src/status/get_handler.dart
+++ b/pkg/analysis_server/lib/src/status/get_handler.dart
@@ -298,106 +298,68 @@
_writePage(buffer, 'Analysis Server - Analysis Performance', [],
(StringBuffer buffer) {
buffer.write('<h3>Analysis Performance</h3>');
-
//
// Write performance tags.
//
- {
- buffer.write('<p><b>Time spent in each phase of analysis</b></p>');
- buffer.write(
- '<table style="border-collapse: separate; border-spacing: 10px 5px;">');
- _writeRow(buffer, ['Time (in ms)', 'Percent', 'Analysis Phase'],
- header: true);
- // prepare sorted tags
- List<PerformanceTag> tags = PerformanceTag.all.toList();
- tags.remove(ServerPerformanceStatistics.idle);
- tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
- // prepare total time
- int totalTime = 0;
- tags.forEach((PerformanceTag tag) {
- totalTime += tag.elapsedMs;
- });
- // write rows
- void writeRow(PerformanceTag tag) {
- double percent = (tag.elapsedMs * 100) / totalTime;
- String percentStr = '${percent.toStringAsFixed(2)}%';
- _writeRow(buffer, [tag.elapsedMs, percentStr, tag.label],
- classes: ["right", "right", null]);
- }
- tags.forEach(writeRow);
- buffer.write('</table>');
+ buffer.write('<p><b>Performance tag data</b></p>');
+ buffer.write(
+ '<table style="border-collapse: separate; border-spacing: 10px 5px;">');
+ _writeRow(buffer, ['Time (in ms)', 'Percent', 'Tag name'],
+ header: true);
+ // prepare sorted tags
+ List<PerformanceTag> tags = PerformanceTag.all.toList();
+ tags.remove(ServerPerformanceStatistics.idle);
+ tags.sort((a, b) => b.elapsedMs - a.elapsedMs);
+ // prepare total time
+ int totalTagTime = 0;
+ tags.forEach((PerformanceTag tag) {
+ totalTagTime += tag.elapsedMs;
+ });
+ // write rows
+ void writeRow(PerformanceTag tag) {
+ double percent = (tag.elapsedMs * 100) / totalTagTime;
+ String percentStr = '${percent.toStringAsFixed(2)}%';
+ _writeRow(buffer, [tag.elapsedMs, percentStr, tag.label],
+ classes: ["right", "right", null]);
}
+ tags.forEach(writeRow);
+ buffer.write('</table>');
+ //
+ // Write task model timing information.
+ //
+ buffer.write('<p><b>Task performace data</b></p>');
+ buffer.write(
+ '<table style="border-collapse: separate; border-spacing: 10px 5px;">');
+ _writeRow(buffer, [
+ 'Task Name',
+ 'Count',
+ 'Total Time (in ms)',
+ 'Average Time (in ms)'
+ ], header: true);
- //
- // Write new task model timing information.
- //
- if (AnalysisEngine.instance.useTaskModel) {
- buffer.write('<p><b>Task performace data</b></p>');
- buffer.write(
- '<table style="border-collapse: separate; border-spacing: 10px 5px;">');
+ Map<Type, int> countMap = AnalysisTask.countMap;
+ Map<Type, Stopwatch> stopwatchMap = AnalysisTask.stopwatchMap;
+ List<Type> taskClasses = stopwatchMap.keys.toList();
+ taskClasses.sort((Type first, Type second) =>
+ first.toString().compareTo(second.toString()));
+ int totalTaskTime = 0;
+ taskClasses.forEach((Type taskClass) {
+ int count = countMap[taskClass];
+ if (count == null) {
+ count = 0;
+ }
+ int taskTime = stopwatchMap[taskClass].elapsedMilliseconds;
+ totalTaskTime += taskTime;
_writeRow(buffer, [
- 'Task Name',
- 'Count',
- 'Total Time (in ms)',
- 'Average Time (in ms)'
- ], header: true);
-
- Map<Type, int> countMap = AnalysisTask.countMap;
- Map<Type, Stopwatch> stopwatchMap = AnalysisTask.stopwatchMap;
- List<Type> taskClasses = stopwatchMap.keys.toList();
- taskClasses.sort((Type first, Type second) =>
- first.toString().compareTo(second.toString()));
- int totalTime = 0;
- taskClasses.forEach((Type taskClass) {
- int count = countMap[taskClass];
- if (count == null) {
- count = 0;
- }
- int taskTime = stopwatchMap[taskClass].elapsedMilliseconds;
- totalTime += taskTime;
- _writeRow(buffer, [
- taskClass.toString(),
- count,
- taskTime,
- count <= 0 ? '-' : (taskTime / count).toStringAsFixed(3)
- ], classes: [null, "right", "right", "right"]);
- });
- _writeRow(buffer, ['Total', '-', totalTime, '-'],
- classes: [null, "right", "right", "right"]);
- buffer.write('</table>');
- }
-
- //
- // Write old task model transition information.
- //
- {
- Map<DataDescriptor, Map<CacheState, int>> transitionMap =
- SourceEntry.transitionMap;
- buffer.write(
- '<p><b>Number of times a state transitioned to VALID (grouped by descriptor)</b></p>');
- if (transitionMap.isEmpty) {
- buffer.write('<p>none</p>');
- } else {
- List<DataDescriptor> descriptors = transitionMap.keys.toList();
- descriptors.sort((DataDescriptor first, DataDescriptor second) =>
- first.toString().compareTo(second.toString()));
- for (DataDescriptor key in descriptors) {
- Map<CacheState, int> countMap = transitionMap[key];
- List<CacheState> oldStates = countMap.keys.toList();
- oldStates.sort((CacheState first, CacheState second) =>
- first.toString().compareTo(second.toString()));
- buffer.write('<p>${key.toString()}</p>');
- buffer.write(
- '<table style="border-collapse: separate; border-spacing: 10px 5px;">');
- _writeRow(buffer, ['Count', 'Previous State'], header: true);
- for (CacheState state in oldStates) {
- _writeRow(buffer, [countMap[state], state.toString()],
- classes: ["right", null]);
- }
- buffer.write('</table>');
- }
- }
- }
+ taskClass.toString(),
+ count,
+ taskTime,
+ count <= 0 ? '-' : (taskTime / count).toStringAsFixed(3)
+ ], classes: [null, "right", "right", "right"]);
+ });
+ _writeRow(buffer, ['Total', '-', totalTaskTime, '-'],
+ classes: [null, "right", "right", "right"]);
+ buffer.write('</table>');
});
});
}
diff --git a/pkg/analysis_server/lib/starter.dart b/pkg/analysis_server/lib/starter.dart
index dae6a05..e96c8b3 100644
--- a/pkg/analysis_server/lib/starter.dart
+++ b/pkg/analysis_server/lib/starter.dart
@@ -5,6 +5,7 @@
library driver;
import 'package:analysis_server/src/server/driver.dart';
+import 'package:analysis_server/uri/resolver_provider.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:plugin/plugin.dart';
@@ -23,6 +24,13 @@
void set instrumentationServer(InstrumentationServer server);
/**
+ * Set the package resolver provider used to override the way package URI's
+ * are resolved in some contexts. The provider should return `null` if the
+ * default package resolution scheme should be used instead.
+ */
+ void set packageResolverProvider(ResolverProvider provider);
+
+ /**
* Set the [plugins] that are defined outside the analysis_server package.
*/
void set userDefinedPlugins(List<Plugin> plugins);
diff --git a/pkg/analysis_server/lib/uri/resolver_provider.dart b/pkg/analysis_server/lib/uri/resolver_provider.dart
new file mode 100644
index 0000000..36ae274
--- /dev/null
+++ b/pkg/analysis_server/lib/uri/resolver_provider.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2015, 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.
+
+library analysis_server.uri.resolver_provider;
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+/**
+ * A function that will return a [UriResolver] that can be used to resolve a
+ * specific kind of URI within the analysis context rooted at the given folder.
+ * This is currently being used to provide a package URI resolver that will be
+ * used by the server (see [ServerStarter.packageResolverProvider]).
+ */
+typedef UriResolver ResolverProvider(Folder folder);
diff --git a/pkg/analysis_server/test/analysis/notification_navigation_test.dart b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
index cc24ca3..16d1bcd 100644
--- a/pkg/analysis_server/test/analysis/notification_navigation_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_navigation_test.dart
@@ -295,8 +295,14 @@
}
''');
return prepareNavigation().then((_) {
- assertHasRegionString('B.named');
- assertHasTarget('named();');
+ {
+ assertHasRegionString('B.named;', 'B'.length);
+ assertHasTarget('named();');
+ }
+ {
+ assertHasRegionString('named;', 'named'.length);
+ assertHasTarget('named();');
+ }
});
}
@@ -320,7 +326,7 @@
assertHasTarget('A {');
}
{
- assertHasRegion('.named;', '.named'.length);
+ assertHasRegion('named;', 'named'.length);
assertHasTarget('named() {}');
}
});
@@ -445,7 +451,7 @@
}
''');
return prepareNavigation().then((_) {
- assertHasRegionString('new A');
+ assertHasRegionString('A()', 'A'.length);
assertHasTarget('A {');
});
}
@@ -460,7 +466,7 @@
''');
return prepareNavigation().then((_) {
{
- assertHasRegion('new B<A>', 'new B'.length);
+ assertHasRegion('B<A>', 'B'.length);
assertHasTarget('B<T> {');
}
{
@@ -480,8 +486,14 @@
}
''');
return prepareNavigation().then((_) {
- assertHasRegionString('new A.named');
- assertHasTarget('named() {}');
+ {
+ assertHasRegionString('A.named();', 'A'.length);
+ assertHasTarget('named() {}');
+ }
+ {
+ assertHasRegionString('named();', 'named'.length);
+ assertHasTarget('named() {}');
+ }
});
}
@@ -497,7 +509,7 @@
''');
return prepareNavigation().then((_) {
{
- assertHasRegionString('new B');
+ assertHasRegionString('B<A>', 'B'.length);
assertHasTarget('named() {}');
}
{
@@ -505,7 +517,7 @@
assertHasTarget('A {');
}
{
- assertHasRegion('.named();', '.named'.length);
+ assertHasRegion('named();', 'named'.length);
assertHasTarget('named() {}');
}
});
@@ -521,7 +533,7 @@
}
''');
return prepareNavigation().then((_) {
- assertHasRegionString('new A');
+ assertHasRegionString('A();', 'A'.length);
assertHasTarget('A() {}', 0);
});
}
@@ -538,7 +550,7 @@
''');
return prepareNavigation().then((_) {
{
- assertHasRegionString('new B');
+ assertHasRegionString('B<A>();', 'B'.length);
assertHasTarget('B() {}', 0);
}
{
diff --git a/pkg/analysis_server/test/analysis/update_content_test.dart b/pkg/analysis_server/test/analysis/update_content_test.dart
index b8c313d..135ce6a 100644
--- a/pkg/analysis_server/test/analysis/update_content_test.dart
+++ b/pkg/analysis_server/test/analysis/update_content_test.dart
@@ -4,6 +4,7 @@
library test.analysis.updateContent;
+import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/constants.dart';
import 'package:analysis_server/src/protocol.dart';
import 'package:analysis_server/src/services/index/index.dart';
@@ -177,6 +178,24 @@
expect(_getUserSources(context2), isEmpty);
}
+ test_removeOverlay_incrementalChange() async {
+ createProject();
+ addTestFile('main() { print(1); }');
+ await server.onAnalysisComplete;
+ CompilationUnit unit = _getTestUnit();
+ // add an overlay
+ server.updateContent(
+ '1', {testFile: new AddContentOverlay('main() { print(2); }')});
+ // it was an incremental change
+ await server.onAnalysisComplete;
+ expect(_getTestUnit(), same(unit));
+ // remove overlay
+ server.updateContent('2', {testFile: new RemoveContentOverlay()});
+ // it was an incremental change
+ await server.onAnalysisComplete;
+ expect(_getTestUnit(), same(unit));
+ }
+
test_sendNoticesAfterNopChange() async {
createProject();
addTestFile('');
@@ -214,6 +233,13 @@
expect(filesErrors, isNotEmpty);
}
+ CompilationUnit _getTestUnit() {
+ ContextSourcePair pair = server.getContextSourcePair(testFile);
+ AnalysisContext context = pair.context;
+ Source source = pair.source;
+ return context.getResolvedCompilationUnit2(source, source);
+ }
+
List<Source> _getUserSources(AnalysisContext context) {
List<Source> sources = <Source>[];
context.sources.forEach((source) {
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index 2908cde..092fc85 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -20,6 +20,7 @@
import 'package:unittest/unittest.dart';
import 'mocks.dart';
+import 'package:analysis_server/uri/resolver_provider.dart';
main() {
groupSep = ' | ';
@@ -59,6 +60,8 @@
MockPackageMapProvider packageMapProvider;
+ UriResolver packageResolver = null;
+
String projPath = '/my/proj';
String newFile(List<String> pathComponents, [String content = '']) {
@@ -73,10 +76,14 @@
return folderPath;
}
+ UriResolver providePackageResolver(Folder folder) {
+ return packageResolver;
+ }
+
void setUp() {
resourceProvider = new MemoryResourceProvider();
packageMapProvider = new MockPackageMapProvider();
- manager = new TestContextManager(resourceProvider, packageMapProvider);
+ manager = new TestContextManager(resourceProvider, providePackageResolver, packageMapProvider);
resourceProvider.newFolder(projPath);
}
@@ -195,6 +202,29 @@
var filePaths = manager.currentContextFilePaths[projPath];
expect(filePaths, hasLength(1));
expect(filePaths, contains(filePath));
+ List<AnalysisContext> contextsInAnalysisRoot = manager.contextsInAnalysisRoot(resourceProvider.newFolder(projPath));
+ expect(contextsInAnalysisRoot, hasLength(1));
+ AnalysisContext context = contextsInAnalysisRoot[0];
+ expect(context, isNotNull);
+ Source result = context.sourceFactory.forUri('package:foo/foo.dart');
+ expect(result, isNotNull);
+ expect(result.exists(), isFalse);
+ }
+
+ void test_setRoots_packageResolver() {
+ Uri uri = Uri.parse('package:foo/foo.dart');
+ Source source = new TestSource();
+ packageResolver = new TestUriResolver({uri : source});
+ String filePath = posix.join(projPath, 'foo.dart');
+ resourceProvider.newFile(filePath, 'contents');
+ manager.setRoots(<String>[projPath], <String>[], <String, String>{});
+
+ List<AnalysisContext> contextsInAnalysisRoot = manager.contextsInAnalysisRoot(resourceProvider.newFolder(projPath));
+ expect(contextsInAnalysisRoot, hasLength(1));
+ AnalysisContext context = contextsInAnalysisRoot[0];
+ expect(context, isNotNull);
+ Source result = context.sourceFactory.forUri2(uri);
+ expect(result, same(source));
}
void test_setRoots_addFolderWithDartFileInSubfolder() {
@@ -981,8 +1011,9 @@
<String, UriResolver>{};
TestContextManager(MemoryResourceProvider resourceProvider,
+ ResolverProvider packageResolverProvider,
OptimizingPubPackageMapProvider packageMapProvider)
- : super(resourceProvider, packageMapProvider,
+ : super(resourceProvider, packageResolverProvider, packageMapProvider,
InstrumentationService.NULL_SERVICE);
/**
@@ -1066,3 +1097,24 @@
currentContextPackageUriResolvers[contextFolder.path] = packageUriResolver;
}
}
+
+class TestUriResolver extends UriResolver {
+ Map<Uri, Source> uriMap;
+
+ TestUriResolver(this.uriMap);
+
+ @override
+ Source resolveAbsolute(Uri uri) {
+ return uriMap[uri];
+ }
+}
+
+/**
+ * A [Source] that knows it's [fullName].
+ */
+class TestSource implements Source {
+ TestSource();
+
+ @override
+ noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
diff --git a/pkg/analysis_server/test/integration/analysis/navigation_test.dart b/pkg/analysis_server/test/integration/analysis/navigation_test.dart
index 87b2e31..310e882 100644
--- a/pkg/analysis_server/test/integration/analysis/navigation_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/navigation_test.dart
@@ -41,7 +41,7 @@
int topLevelVariable;
main() {
- Class<int> localVariable = new Class<int>.constructor();
+ Class<int> localVariable = new Class<int>.constructor(); // usage
function(() => localVariable.field);
localVariable.method();
localVariable.field = 1;
@@ -101,7 +101,10 @@
checkLocal('Class<int>', 'Class<TypeParameter>', ElementKind.CLASS);
checkRemote(
"part 'test2.dart';", r'test2.dart$', ElementKind.COMPILATION_UNIT);
- checkLocal('new Class<int>.constructor',
+ checkLocal('Class<int>.constructor',
+ 'constructor(); /* constructor declaration */',
+ ElementKind.CONSTRUCTOR);
+ checkLocal('constructor(); // usage',
'constructor(); /* constructor declaration */',
ElementKind.CONSTRUCTOR);
checkLocal('field;', 'field;', ElementKind.FIELD);
diff --git a/pkg/analysis_server/test/integration/integration_tests.dart b/pkg/analysis_server/test/integration/integration_tests.dart
index 3b7e13a..faec89f 100644
--- a/pkg/analysis_server/test/integration/integration_tests.dart
+++ b/pkg/analysis_server/test/integration/integration_tests.dart
@@ -462,7 +462,7 @@
* upward to the 'test' dir, and then going up one more directory.
*/
String findRoot(String pathname) {
- while (basename(pathname) != 'test') {
+ while (!['benchmark', 'test'].contains(basename(pathname))) {
String parent = dirname(pathname);
if (parent.length >= pathname.length) {
throw new Exception("Can't find root directory");
@@ -584,7 +584,7 @@
* `true`, the server will be started with "--observe" and
* "--pause-isolates-on-exit", allowing the observatory to be used.
*/
- Future start({bool debugServer: false, bool profileServer: false}) {
+ Future start({bool debugServer: false, int diagnosticPort, bool profileServer: false}) {
if (_process != null) {
throw new Exception('Process already started');
}
@@ -606,6 +606,10 @@
}
arguments.add('--checked');
arguments.add(serverPath);
+ if (diagnosticPort != null) {
+ arguments.add('--port');
+ arguments.add(diagnosticPort.toString());
+ }
return Process.start(dartBinary, arguments).then((Process process) {
_process = process;
process.exitCode.then((int code) {
diff --git a/pkg/analysis_server/test/services/correction/assist_test.dart b/pkg/analysis_server/test/services/correction/assist_test.dart
index 57f49d0..8e27fa5 100644
--- a/pkg/analysis_server/test/services/correction/assist_test.dart
+++ b/pkg/analysis_server/test/services/correction/assist_test.dart
@@ -939,6 +939,95 @@
assertNoAssistAt('fff()', DartAssistKind.CONVERT_INTO_EXPRESSION_BODY);
}
+ void test_convertToFieldParameter_BAD_additionalUse() {
+ resolveTestUnit('''
+class A {
+ int aaa2;
+ int bbb2;
+ A(int aaa) : aaa2 = aaa, bbb2 = aaa;
+}
+''');
+ assertNoAssistAt('aaa)', DartAssistKind.CONVERT_TO_FIELD_PARAMETER);
+ }
+
+ void test_convertToFieldParameter_BAD_notPureAssignment() {
+ resolveTestUnit('''
+class A {
+ int aaa2;
+ A(int aaa) : aaa2 = aaa * 2;
+}
+''');
+ assertNoAssistAt('aaa)', DartAssistKind.CONVERT_TO_FIELD_PARAMETER);
+ }
+
+ void test_convertToFieldParameter_OK_firstInitializer() {
+ resolveTestUnit('''
+class A {
+ double aaa2;
+ int bbb2;
+ A(int aaa, int bbb) : aaa2 = aaa, bbb2 = bbb;
+}
+''');
+ assertHasAssistAt('aaa, ', DartAssistKind.CONVERT_TO_FIELD_PARAMETER, '''
+class A {
+ double aaa2;
+ int bbb2;
+ A(this.aaa2, int bbb) : bbb2 = bbb;
+}
+''');
+ }
+
+ void test_convertToFieldParameter_OK_onParameterName_inInitializer() {
+ resolveTestUnit('''
+class A {
+ int test2;
+ A(int test) : test2 = test {
+ }
+}
+''');
+ assertHasAssistAt('test {', DartAssistKind.CONVERT_TO_FIELD_PARAMETER, '''
+class A {
+ int test2;
+ A(this.test2) {
+ }
+}
+''');
+ }
+
+ void test_convertToFieldParameter_OK_onParameterName_inParameters() {
+ resolveTestUnit('''
+class A {
+ int test;
+ A(int test) : test = test {
+ }
+}
+''');
+ assertHasAssistAt('test)', DartAssistKind.CONVERT_TO_FIELD_PARAMETER, '''
+class A {
+ int test;
+ A(this.test) {
+ }
+}
+''');
+ }
+
+ void test_convertToFieldParameter_OK_secondInitializer() {
+ resolveTestUnit('''
+class A {
+ double aaa2;
+ int bbb2;
+ A(int aaa, int bbb) : aaa2 = aaa, bbb2 = bbb;
+}
+''');
+ assertHasAssistAt('bbb)', DartAssistKind.CONVERT_TO_FIELD_PARAMETER, '''
+class A {
+ double aaa2;
+ int bbb2;
+ A(int aaa, this.bbb2) : aaa2 = aaa;
+}
+''');
+ }
+
void test_convertToForIndex_BAD_bodyNotBlock() {
resolveTestUnit('''
main(List<String> items) {
@@ -1339,6 +1428,57 @@
assertNoAssistAt('isEven;', DartAssistKind.CONVERT_INTO_IS_NOT_EMPTY);
}
+ void test_convertToNormalParameter_OK_dynamic() {
+ resolveTestUnit('''
+class A {
+ var test;
+ A(this.test) {
+ }
+}
+''');
+ assertHasAssistAt('test)', DartAssistKind.CONVERT_TO_NORMAL_PARAMETER, '''
+class A {
+ var test;
+ A(test) : test = test {
+ }
+}
+''');
+ }
+
+ void test_convertToNormalParameter_OK_firstInitializer() {
+ resolveTestUnit('''
+class A {
+ int test;
+ A(this.test) {
+ }
+}
+''');
+ assertHasAssistAt('test)', DartAssistKind.CONVERT_TO_NORMAL_PARAMETER, '''
+class A {
+ int test;
+ A(int test) : test = test {
+ }
+}
+''');
+ }
+
+ void test_convertToNormalParameter_OK_secondInitializer() {
+ resolveTestUnit('''
+class A {
+ double aaa;
+ int bbb;
+ A(this.bbb) : aaa = 1.0;
+}
+''');
+ assertHasAssistAt('bbb)', DartAssistKind.CONVERT_TO_NORMAL_PARAMETER, '''
+class A {
+ double aaa;
+ int bbb;
+ A(int bbb) : aaa = 1.0, bbb = bbb;
+}
+''');
+ }
+
void test_encapsulateField_BAD_alreadyPrivate() {
resolveTestUnit('''
class A {
diff --git a/pkg/analysis_server/test/socket_server_test.dart b/pkg/analysis_server/test/socket_server_test.dart
index e98aa2a..8e74cef 100644
--- a/pkg/analysis_server/test/socket_server_test.dart
+++ b/pkg/analysis_server/test/socket_server_test.dart
@@ -111,7 +111,7 @@
manager.processPlugins([serverPlugin]);
return new SocketServer(new AnalysisServerOptions(),
DirectoryBasedDartSdk.defaultSdk, InstrumentationService.NULL_SERVICE,
- serverPlugin);
+ serverPlugin, null);
}
}
diff --git a/pkg/analysis_server/tool/spec/codegen_tools.dart b/pkg/analysis_server/tool/spec/codegen_tools.dart
index 84a95ab..0d7e95f 100644
--- a/pkg/analysis_server/tool/spec/codegen_tools.dart
+++ b/pkg/analysis_server/tool/spec/codegen_tools.dart
@@ -286,7 +286,11 @@
String expectedContents = fileContentsComputer();
File outputFile =
new File(joinAll(posix.split(posix.join(outputDirPath, file))));
- if (expectedContents != outputFile.readAsStringSync()) {
+ String actualContents = outputFile.readAsStringSync();
+ // Normalize Windows line endings to Unix line endings so that the
+ // comparison doesn't fail on Windows.
+ actualContents = actualContents.replaceAll('\r\n', '\n');
+ if (expectedContents != actualContents) {
return false;
}
}
@@ -370,7 +374,11 @@
bool check() {
String expectedContents = computeContents();
try {
- return expectedContents == outputFile.readAsStringSync();
+ String actualContents = outputFile.readAsStringSync();
+ // Normalize Windows line endings to Unix line endings so that the
+ // comparison doesn't fail on Windows.
+ actualContents = actualContents.replaceAll('\r\n', '\n');
+ return expectedContents == actualContents;
} catch (e) {
// There was a problem reading the file (most likely because it didn't
// exist). Treat that the same as if the file doesn't have the expected
diff --git a/pkg/analyzer/lib/file_system/file_system.dart b/pkg/analyzer/lib/file_system/file_system.dart
index 62c158b..899d6c5 100644
--- a/pkg/analyzer/lib/file_system/file_system.dart
+++ b/pkg/analyzer/lib/file_system/file_system.dart
@@ -139,6 +139,20 @@
Context get pathContext;
/**
+ * Return a [File] that corresponds to the given [path].
+ *
+ * A file may or may not exist at this location.
+ */
+ File getFile(String path);
+
+ /**
+ * Return a [Folder] that corresponds to the given [path].
+ *
+ * A folder may or may not exist at this location.
+ */
+ Folder getFolder(String path);
+
+ /**
* Return the [Resource] that corresponds to the given [path].
*/
Resource getResource(String path);
diff --git a/pkg/analyzer/lib/file_system/memory_file_system.dart b/pkg/analyzer/lib/file_system/memory_file_system.dart
index b0a5469..8ce2472 100644
--- a/pkg/analyzer/lib/file_system/memory_file_system.dart
+++ b/pkg/analyzer/lib/file_system/memory_file_system.dart
@@ -65,6 +65,12 @@
}
@override
+ File getFile(String path) => new _MemoryFile(this, path);
+
+ @override
+ Folder getFolder(String path) => newFolder(path);
+
+ @override
Resource getResource(String path) {
path = posix.normalize(path);
Resource resource = _pathToResource[path];
diff --git a/pkg/analyzer/lib/file_system/physical_file_system.dart b/pkg/analyzer/lib/file_system/physical_file_system.dart
index d9497df..b8fdcf1 100644
--- a/pkg/analyzer/lib/file_system/physical_file_system.dart
+++ b/pkg/analyzer/lib/file_system/physical_file_system.dart
@@ -41,13 +41,17 @@
Context get pathContext => io.Platform.isWindows ? windows : posix;
@override
+ File getFile(String path) => new _PhysicalFile(new io.File(path));
+
+ @override
+ Folder getFolder(String path) => new _PhysicalFolder(new io.Directory(path));
+
+ @override
Resource getResource(String path) {
if (io.FileSystemEntity.isDirectorySync(path)) {
- io.Directory directory = new io.Directory(path);
- return new _PhysicalFolder(directory);
+ return getFolder(path);
} else {
- io.File file = new io.File(path);
- return new _PhysicalFile(file);
+ return getFile(path);
}
}
diff --git a/pkg/analyzer/lib/src/context/cache.dart b/pkg/analyzer/lib/src/context/cache.dart
index b211067..0d67817 100644
--- a/pkg/analyzer/lib/src/context/cache.dart
+++ b/pkg/analyzer/lib/src/context/cache.dart
@@ -319,6 +319,22 @@
List<ResultDescriptor> get nonInvalidResults => _resultMap.keys.toList();
/**
+ * Notifies the entry that the client is going to stop using it.
+ */
+ void dispose() {
+ _resultMap.forEach((descriptor, data) {
+ TargetedResult result = new TargetedResult(target, descriptor);
+ for (TargetedResult dependedOnResult in data.dependedOnResults) {
+ ResultData dependedOnData = _partition._getDataFor(dependedOnResult);
+ if (dependedOnData != null) {
+ dependedOnData.dependentResults.remove(result);
+ }
+ }
+ });
+ _resultMap.clear();
+ }
+
+ /**
* Fix the state of the [exception] to match the current state of the entry.
*/
void fixExceptionState() {
@@ -520,7 +536,7 @@
// Stop depending on other results.
TargetedResult thisResult = new TargetedResult(target, descriptor);
for (TargetedResult dependedOnResult in thisData.dependedOnResults) {
- ResultData data = _partition._getDataFor(dependedOnResult, orNull: true);
+ ResultData data = _partition._getDataFor(dependedOnResult);
if (data != null) {
data.dependentResults.remove(thisResult);
}
@@ -559,14 +575,14 @@
void _setDependedOnResults(ResultData thisData, TargetedResult thisResult,
List<TargetedResult> dependedOn) {
thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) {
- ResultData data = _partition._getDataFor(dependedOnResult, orNull: true);
+ ResultData data = _partition._getDataFor(dependedOnResult);
if (data != null) {
data.dependentResults.remove(thisResult);
}
});
thisData.dependedOnResults = dependedOn;
thisData.dependedOnResults.forEach((TargetedResult dependedOnResult) {
- ResultData data = _partition._getDataFor(dependedOnResult, orNull: true);
+ ResultData data = _partition._getDataFor(dependedOnResult);
if (data != null) {
data.dependentResults.add(thisResult);
}
@@ -829,6 +845,16 @@
_onResultInvalidated.stream;
/**
+ * Notifies the partition that the client is going to stop using it.
+ */
+ void dispose() {
+ for (CacheEntry entry in _targetMap.values) {
+ entry.dispose();
+ }
+ _targetMap.clear();
+ }
+
+ /**
* Return the entry associated with the given [target].
*/
CacheEntry get(AnalysisTarget target) => _targetMap[target];
@@ -929,13 +955,9 @@
}
}
- ResultData _getDataFor(TargetedResult result, {bool orNull: false}) {
+ ResultData _getDataFor(TargetedResult result) {
CacheEntry entry = context.analysisCache.get(result.target);
- if (orNull) {
- return entry != null ? entry._resultMap[result.result] : null;
- } else {
- return entry.getResultData(result.result);
- }
+ return entry != null ? entry._resultMap[result.result] : null;
}
/**
diff --git a/pkg/analyzer/lib/src/context/context.dart b/pkg/analyzer/lib/src/context/context.dart
index a0e7f1f..095c293 100644
--- a/pkg/analyzer/lib/src/context/context.dart
+++ b/pkg/analyzer/lib/src/context/context.dart
@@ -678,6 +678,7 @@
}
}
_pendingFutureTargets.clear();
+ _privatePartition.dispose();
}
@override
@@ -954,7 +955,7 @@
}
bool changed = newContents != originalContents;
if (newContents != null) {
- if (newContents != originalContents) {
+ if (changed) {
if (!analysisOptions.incremental ||
!_tryPoorMansIncrementalResolution(source, newContents)) {
_sourceChanged(source);
@@ -965,22 +966,24 @@
entry.modificationTime = _contentCache.getModificationStamp(source);
}
} else if (originalContents != null) {
- changed = newContents != originalContents;
// We are removing the overlay for the file, check if the file's
// contents is the same as it was in the overlay.
try {
TimestampedData<String> fileContents = getContents(source);
- String fileContentsData = fileContents.data;
- if (fileContentsData == originalContents) {
- entry.setValue(CONTENT, fileContentsData, TargetedResult.EMPTY_LIST);
- entry.modificationTime = fileContents.modificationTime;
+ newContents = fileContents.data;
+ entry.modificationTime = fileContents.modificationTime;
+ if (newContents == originalContents) {
+ entry.setValue(CONTENT, newContents, TargetedResult.EMPTY_LIST);
changed = false;
}
} catch (e) {}
// If not the same content (e.g. the file is being closed without save),
// then force analysis.
if (changed) {
- _sourceChanged(source);
+ if (!analysisOptions.incremental ||
+ !_tryPoorMansIncrementalResolution(source, newContents)) {
+ _sourceChanged(source);
+ }
}
}
if (notify && changed) {
@@ -1094,7 +1097,6 @@
setValue(LIBRARY_ELEMENT3, library);
setValue(LIBRARY_ELEMENT4, library);
setValue(LIBRARY_ELEMENT5, library);
- setValue(LIBRARY_ELEMENT6, library);
setValue(LINE_INFO, new LineInfo(<int>[0]));
setValue(PARSE_ERRORS, AnalysisError.NO_ERRORS);
entry.setState(PARSED_UNIT, CacheState.FLUSHED);
@@ -1718,8 +1720,8 @@
dartDelta.hasDirectiveChange = unitDelta.hasDirectiveChange;
unitDelta.addedDeclarations.forEach(dartDelta.elementAdded);
unitDelta.removedDeclarations.forEach(dartDelta.elementRemoved);
- print(
- 'dartDelta: add=${dartDelta.addedNames} remove=${dartDelta.removedNames}');
+// print(
+// 'dartDelta: add=${dartDelta.addedNames} remove=${dartDelta.removedNames}');
delta = dartDelta;
entry.setState(CONTENT, CacheState.INVALID, delta: delta);
return;
diff --git a/pkg/analyzer/lib/src/generated/element.dart b/pkg/analyzer/lib/src/generated/element.dart
index 52ef4f5..24344aa 100644
--- a/pkg/analyzer/lib/src/generated/element.dart
+++ b/pkg/analyzer/lib/src/generated/element.dart
@@ -501,9 +501,15 @@
List<PropertyAccessorElement> _accessors = PropertyAccessorElement.EMPTY_LIST;
/**
- * A list containing all of the constructors contained in this class.
+ * For classes which are not mixin applications, a list containing all of the
+ * constructors contained in this class, or `null` if the list of
+ * constructors has not yet been built.
+ *
+ * For classes which are mixin applications, the list of constructors is
+ * computed on the fly by the [constructors] getter, and this field is
+ * `null`.
*/
- List<ConstructorElement> _constructors = ConstructorElement.EMPTY_LIST;
+ List<ConstructorElement> _constructors;
/**
* A list containing all of the fields contained in this class.
@@ -586,12 +592,24 @@
}
@override
- List<ConstructorElement> get constructors => _constructors;
+ List<ConstructorElement> get constructors {
+ if (!isMixinApplication) {
+ assert(_constructors != null);
+ return _constructors == null
+ ? ConstructorElement.EMPTY_LIST
+ : _constructors;
+ }
+
+ return _computeMixinAppConstructors();
+ }
/**
* Set the constructors contained in this class to the given [constructors].
+ *
+ * Should only be used for class elements that are not mixin applications.
*/
void set constructors(List<ConstructorElement> constructors) {
+ assert(!isMixinApplication);
for (ConstructorElement constructor in constructors) {
(constructor as ConstructorElementImpl).enclosingElement = this;
}
@@ -599,6 +617,50 @@
}
/**
+ * Return `true` if [CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS] should
+ * be reported for this class.
+ */
+ bool get doesMixinLackConstructors {
+ if (!isMixinApplication && mixins.isEmpty) {
+ // This class is not a mixin application and it doesn't have a "with"
+ // clause, so CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS is
+ // inapplicable.
+ return false;
+ }
+ if (supertype == null) {
+ // Should never happen, since Object is the only class that has no
+ // supertype, and it should have been caught by the test above.
+ assert(false);
+ return false;
+ }
+ // Find the nearest class in the supertype chain that is not a mixin
+ // application.
+ ClassElement nearestNonMixinClass = supertype.element;
+ if (nearestNonMixinClass.isMixinApplication) {
+ // Use a list to keep track of the classes we've seen, so that we won't
+ // go into an infinite loop in the event of a non-trivial loop in the
+ // class hierarchy.
+ List<ClassElementImpl> classesSeen = <ClassElementImpl>[this];
+ while (nearestNonMixinClass.isMixinApplication) {
+ if (classesSeen.contains(nearestNonMixinClass)) {
+ // Loop in the class hierarchy (which is reported elsewhere). Don't
+ // confuse the user with further errors.
+ return false;
+ }
+ classesSeen.add(nearestNonMixinClass);
+ if (nearestNonMixinClass.supertype == null) {
+ // Should never happen, since Object is the only class that has no
+ // supertype, and it is not a mixin application.
+ assert(false);
+ return false;
+ }
+ nearestNonMixinClass = nearestNonMixinClass.supertype.element;
+ }
+ }
+ return !nearestNonMixinClass.constructors.any(isSuperConstructorAccessible);
+ }
+
+ /**
* Set whether this class is defined by an enum declaration.
*/
void set enum2(bool isEnum) {
@@ -732,16 +794,6 @@
setModifier(Modifier.MIXIN_APPLICATION, isMixinApplication);
}
- bool get mixinErrorsReported => hasModifier(Modifier.MIXIN_ERRORS_REPORTED);
-
- /**
- * Set whether an error has reported explaining why this class is an
- * invalid mixin application.
- */
- void set mixinErrorsReported(bool value) {
- setModifier(Modifier.MIXIN_ERRORS_REPORTED, value);
- }
-
@override
List<TypeParameterElement> get typeParameters => _typeParameters;
@@ -996,6 +1048,103 @@
}
}
+ /**
+ * Compute a list of constructors for this class, which is a mixin
+ * application. If specified, [visitedClasses] is a list of the other mixin
+ * application classes which have been visited on the way to reaching this
+ * one (this is used to detect circularities).
+ */
+ List<ConstructorElement> _computeMixinAppConstructors(
+ [List<ClassElementImpl> visitedClasses = null]) {
+ // First get the list of constructors of the superclass which need to be
+ // forwarded to this class.
+ Iterable<ConstructorElement> constructorsToForward;
+ if (supertype == null) {
+ // Shouldn't ever happen, since the only class with no supertype is
+ // Object, and it isn't a mixin application. But for safety's sake just
+ // assume an empty list.
+ assert(false);
+ constructorsToForward = <ConstructorElement>[];
+ } else if (!supertype.element.isMixinApplication) {
+ List<ConstructorElement> superclassConstructors =
+ supertype.element.constructors;
+ // Filter out any constructors with optional parameters (see
+ // dartbug.com/15101).
+ constructorsToForward =
+ superclassConstructors.where(isSuperConstructorAccessible);
+ } else {
+ if (visitedClasses == null) {
+ visitedClasses = <ClassElementImpl>[this];
+ } else {
+ if (visitedClasses.contains(this)) {
+ // Loop in the class hierarchy. Don't try to forward any
+ // constructors.
+ return <ConstructorElement>[];
+ }
+ visitedClasses.add(this);
+ }
+ try {
+ ClassElementImpl superclass = supertype.element;
+ constructorsToForward =
+ superclass._computeMixinAppConstructors(visitedClasses);
+ } finally {
+ visitedClasses.removeLast();
+ }
+ }
+
+ // Figure out the type parameter substitution we need to perform in order
+ // to produce constructors for this class. We want to be robust in the
+ // face of errors, so drop any extra type arguments and fill in any missing
+ // ones with `dynamic`.
+ List<DartType> parameterTypes =
+ TypeParameterTypeImpl.getTypes(supertype.typeParameters);
+ List<DartType> argumentTypes = new List<DartType>.filled(
+ parameterTypes.length, DynamicTypeImpl.instance);
+ for (int i = 0; i < supertype.typeArguments.length; i++) {
+ if (i >= argumentTypes.length) {
+ break;
+ }
+ argumentTypes[i] = supertype.typeArguments[i];
+ }
+
+ // Now create an implicit constructor for every constructor found above,
+ // substituting type parameters as appropriate.
+ return constructorsToForward
+ .map((ConstructorElement superclassConstructor) {
+ ConstructorElementImpl implicitConstructor =
+ new ConstructorElementImpl(superclassConstructor.name, -1);
+ implicitConstructor.synthetic = true;
+ implicitConstructor.redirectedConstructor = superclassConstructor;
+ implicitConstructor.const2 = superclassConstructor.isConst;
+ implicitConstructor.returnType = type;
+ List<ParameterElement> superParameters = superclassConstructor.parameters;
+ int count = superParameters.length;
+ if (count > 0) {
+ List<ParameterElement> implicitParameters =
+ new List<ParameterElement>(count);
+ for (int i = 0; i < count; i++) {
+ ParameterElement superParameter = superParameters[i];
+ ParameterElementImpl implicitParameter =
+ new ParameterElementImpl(superParameter.name, -1);
+ implicitParameter.const3 = superParameter.isConst;
+ implicitParameter.final2 = superParameter.isFinal;
+ implicitParameter.parameterKind = superParameter.parameterKind;
+ implicitParameter.synthetic = true;
+ implicitParameter.type =
+ superParameter.type.substitute2(argumentTypes, parameterTypes);
+ implicitParameters[i] = implicitParameter;
+ }
+ implicitConstructor.parameters = implicitParameters;
+ }
+ FunctionTypeImpl constructorType =
+ new FunctionTypeImpl(implicitConstructor);
+ constructorType.typeArguments = type.typeArguments;
+ implicitConstructor.type = constructorType;
+ implicitConstructor.enclosingElement = this;
+ return implicitConstructor;
+ }).toList();
+ }
+
PropertyAccessorElement _internalLookUpConcreteGetter(
String getterName, LibraryElement library, bool includeThisClass) {
PropertyAccessorElement getter =
@@ -8073,41 +8222,34 @@
const Modifier('MIXIN_APPLICATION', 12);
/**
- * Indicates that an error has reported explaining why this class is an
- * invalid mixin application.
- */
- static const Modifier MIXIN_ERRORS_REPORTED =
- const Modifier('MIXIN_ERRORS_REPORTED', 13);
-
- /**
* Indicates that the value of a parameter or local variable might be mutated
* within the context.
*/
static const Modifier POTENTIALLY_MUTATED_IN_CONTEXT =
- const Modifier('POTENTIALLY_MUTATED_IN_CONTEXT', 14);
+ const Modifier('POTENTIALLY_MUTATED_IN_CONTEXT', 13);
/**
* Indicates that the value of a parameter or local variable might be mutated
* within the scope.
*/
static const Modifier POTENTIALLY_MUTATED_IN_SCOPE =
- const Modifier('POTENTIALLY_MUTATED_IN_SCOPE', 15);
+ const Modifier('POTENTIALLY_MUTATED_IN_SCOPE', 14);
/**
* Indicates that a class contains an explicit reference to 'super'.
*/
static const Modifier REFERENCES_SUPER =
- const Modifier('REFERENCES_SUPER', 16);
+ const Modifier('REFERENCES_SUPER', 15);
/**
* Indicates that the pseudo-modifier 'set' was applied to the element.
*/
- static const Modifier SETTER = const Modifier('SETTER', 17);
+ static const Modifier SETTER = const Modifier('SETTER', 16);
/**
* Indicates that the modifier 'static' was applied to the element.
*/
- static const Modifier STATIC = const Modifier('STATIC', 18);
+ static const Modifier STATIC = const Modifier('STATIC', 17);
/**
* Indicates that the element does not appear in the source code but was
@@ -8115,7 +8257,7 @@
* constructors, an implicit zero-argument constructor will be created and it
* will be marked as being synthetic.
*/
- static const Modifier SYNTHETIC = const Modifier('SYNTHETIC', 19);
+ static const Modifier SYNTHETIC = const Modifier('SYNTHETIC', 18);
static const List<Modifier> values = const [
ABSTRACT,
@@ -8131,7 +8273,6 @@
HAS_EXT_URI,
MIXIN,
MIXIN_APPLICATION,
- MIXIN_ERRORS_REPORTED,
POTENTIALLY_MUTATED_IN_CONTEXT,
POTENTIALLY_MUTATED_IN_SCOPE,
REFERENCES_SUPER,
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index c047654..b93931f 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -1049,7 +1049,7 @@
ConstructorElement element =
superType.lookUpConstructor(superName, _definingLibrary);
if (element == null ||
- (!enclosingClass.mixinErrorsReported &&
+ (!enclosingClass.doesMixinLackConstructors &&
!enclosingClass.isSuperConstructorAccessible(element))) {
if (name != null) {
_resolver.reportErrorForNode(
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index 7d3f7e6..5195922 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -2170,7 +2170,7 @@
}
bool changed = newContents != originalContents;
if (newContents != null) {
- if (newContents != originalContents) {
+ if (changed) {
_incrementalAnalysisCache =
IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
if (!analysisOptions.incremental ||
@@ -2187,22 +2187,24 @@
} else if (originalContents != null) {
_incrementalAnalysisCache =
IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
- changed = newContents != originalContents;
// We are removing the overlay for the file, check if the file's
// contents is the same as it was in the overlay.
try {
TimestampedData<String> fileContents = getContents(source);
- String fileContentsData = fileContents.data;
- if (fileContentsData == originalContents) {
- sourceEntry.setValue(SourceEntry.CONTENT, fileContentsData);
- sourceEntry.modificationTime = fileContents.modificationTime;
+ newContents = fileContents.data;
+ sourceEntry.modificationTime = fileContents.modificationTime;
+ if (newContents == originalContents) {
+ sourceEntry.setValue(SourceEntry.CONTENT, newContents);
changed = false;
}
} catch (e) {}
// If not the same content (e.g. the file is being closed without save),
// then force analysis.
if (changed) {
- _sourceChanged(source);
+ if (!analysisOptions.incremental ||
+ !_tryPoorMansIncrementalResolution(source, newContents)) {
+ _sourceChanged(source);
+ }
}
}
if (notify && changed) {
@@ -2222,7 +2224,7 @@
// Prepare sources to invalidate hints in.
List<Source> sources = <Source>[librarySource];
sources.addAll(dartEntry.getValue(DartEntry.INCLUDED_PARTS));
- // Invalidate hints.
+ // Invalidate hints and lints.
for (Source source in sources) {
DartEntry dartEntry = _cache.get(source);
if (dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource) ==
@@ -2230,6 +2232,11 @@
dartEntry.setStateInLibrary(
DartEntry.HINTS, librarySource, CacheState.INVALID);
}
+ if (dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource) ==
+ CacheState.VALID) {
+ dartEntry.setStateInLibrary(
+ DartEntry.LINTS, librarySource, CacheState.INVALID);
+ }
}
}
@@ -10728,7 +10735,7 @@
// Resolve the type names.
//
RecordingErrorListener errorListener = new RecordingErrorListener();
- TypeResolverVisitor typeResolverVisitor = new TypeResolverVisitor.con2(
+ TypeResolverVisitor typeResolverVisitor = new TypeResolverVisitor(
_libraryElement, source, typeProvider, errorListener);
unit.accept(typeResolverVisitor);
//
@@ -10736,8 +10743,9 @@
//
InheritanceManager inheritanceManager =
new InheritanceManager(_libraryElement);
- ResolverVisitor resolverVisitor = new ResolverVisitor.con2(_libraryElement,
- source, typeProvider, inheritanceManager, errorListener);
+ ResolverVisitor resolverVisitor = new ResolverVisitor(
+ _libraryElement, source, typeProvider, errorListener,
+ inheritanceManager: inheritanceManager);
unit.accept(resolverVisitor);
//
// Perform additional error checking.
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 85ea901..e80e02a 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -422,6 +422,7 @@
_checkForConflictingInstanceGetterAndSuperclassMember();
_checkImplementsSuperClass(node);
_checkImplementsFunctionWithoutCall(node);
+ _checkForMixinHasNoConstructors(node);
}
}
visitClassDeclarationIncrementally(node);
@@ -474,6 +475,7 @@
_checkForImplementsDeferredClass(implementsClause);
_checkForRecursiveInterfaceInheritance(_enclosingClass);
_checkForNonAbstractClassInheritsAbstractMember(node.name);
+ _checkForMixinHasNoConstructors(node);
}
} finally {
_enclosingClass = outerClassElement;
@@ -4165,6 +4167,18 @@
}
/**
+ * Report the error [CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS] if
+ * appropriate.
+ */
+ void _checkForMixinHasNoConstructors(AstNode node) {
+ if ((_enclosingClass as ClassElementImpl).doesMixinLackConstructors) {
+ ErrorCode errorCode = CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS;
+ _errorReporter.reportErrorForNode(
+ errorCode, node, [_enclosingClass.supertype]);
+ }
+ }
+
+ /**
* Verify that the given mixin has the 'Object' superclass. The [mixinName] is
* the node to report problem on. The [mixinElement] is the mixing to
* evaluate.
@@ -4290,7 +4304,7 @@
ClassDeclaration declaration) {
// do nothing if mixin errors have already been reported for this class.
ClassElementImpl enclosingClass = _enclosingClass;
- if (enclosingClass.mixinErrorsReported) {
+ if (enclosingClass.doesMixinLackConstructors) {
return false;
}
// do nothing if there is explicit constructor
@@ -5183,7 +5197,7 @@
}
// do nothing if mixin errors have already been reported for this class.
ClassElementImpl enclosingClass = _enclosingClass;
- if (enclosingClass.mixinErrorsReported) {
+ if (enclosingClass.doesMixinLackConstructors) {
return false;
}
//
diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
index 3cc0626..33f470f 100644
--- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
@@ -184,7 +184,6 @@
_enclosingClass = element;
_processElement(element);
_assertSameTypeParameters(node.typeParameters, element.typeParameters);
- _processElement(element.unnamedConstructor);
super.visitClassTypeAlias(node);
}
@@ -900,7 +899,6 @@
List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS;
List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS;
- List<AnalysisError> _lints = AnalysisError.NO_ERRORS;
/**
* Initialize a newly created incremental resolver to resolve a node in the
@@ -942,7 +940,6 @@
// verify
_verify(rootNode);
_context.invalidateLibraryHints(_librarySource);
- _generateLints(rootNode);
// update entry errors
_updateEntry();
// notify unit
@@ -1054,24 +1051,6 @@
throw new AnalysisException("Cannot resolve node: no resolvable node");
}
- void _generateLints(AstNode node) {
- LoggingTimer timer = logger.startTimer();
- try {
- if (_context.analysisOptions.lint) {
- RecordingErrorListener errorListener = new RecordingErrorListener();
- CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit);
- LintGenerator lintGenerator =
- new LintGenerator(<CompilationUnit>[unit], errorListener);
- lintGenerator.generate();
- _lints = errorListener.getErrorsForSource(_source);
- } else {
- _lints = AnalysisError.NO_ERRORS;
- }
- } finally {
- timer.stop('generate lints');
- }
- }
-
/**
* Return the element defined by [node], or `null` if the node does not
* define an element.
@@ -1099,20 +1078,23 @@
Scope scope = _resolutionContext.scope;
// resolve types
{
- TypeResolverVisitor visitor = new TypeResolverVisitor.con3(
- _definingLibrary, _source, _typeProvider, scope, errorListener);
+ TypeResolverVisitor visitor = new TypeResolverVisitor(
+ _definingLibrary, _source, _typeProvider, errorListener,
+ nameScope: scope);
node.accept(visitor);
}
// resolve variables
{
- VariableResolverVisitor visitor = new VariableResolverVisitor.con2(
- _definingLibrary, _source, _typeProvider, scope, errorListener);
+ VariableResolverVisitor visitor = new VariableResolverVisitor(
+ _definingLibrary, _source, _typeProvider, errorListener,
+ nameScope: scope);
node.accept(visitor);
}
// resolve references
{
- ResolverVisitor visitor = new ResolverVisitor.con3(
- _definingLibrary, _source, _typeProvider, scope, errorListener);
+ ResolverVisitor visitor = new ResolverVisitor(
+ _definingLibrary, _source, _typeProvider, errorListener,
+ nameScope: scope);
if (_resolutionContext.enclosingClassDeclaration != null) {
visitor.visitClassDeclarationIncrementally(
_resolutionContext.enclosingClassDeclaration);
@@ -1202,21 +1184,8 @@
}
void _updateEntry_OLD() {
- {
- List<AnalysisError> oldErrors = oldEntry.getValueInLibrary(
- DartEntry.RESOLUTION_ERRORS, _librarySource);
- List<AnalysisError> errors = _updateErrors(oldErrors, _resolveErrors);
- oldEntry.setValueInLibrary(
- DartEntry.RESOLUTION_ERRORS, _librarySource, errors);
- }
- {
- List<AnalysisError> oldErrors = oldEntry.getValueInLibrary(
- DartEntry.VERIFICATION_ERRORS, _librarySource);
- List<AnalysisError> errors = _updateErrors(oldErrors, _verifyErrors);
- oldEntry.setValueInLibrary(
- DartEntry.VERIFICATION_ERRORS, _librarySource, errors);
- }
- oldEntry.setValueInLibrary(DartEntry.LINTS, _librarySource, _lints);
+ _updateErrors_OLD(DartEntry.RESOLUTION_ERRORS, _resolveErrors);
+ _updateErrors_OLD(DartEntry.VERIFICATION_ERRORS, _verifyErrors);
}
List<AnalysisError> _updateErrors(
@@ -1250,6 +1219,14 @@
newUnitEntry.setValueIncremental(descriptor, errors);
}
+ void _updateErrors_OLD(DataDescriptor<List<AnalysisError>> descriptor,
+ List<AnalysisError> newErrors) {
+ List<AnalysisError> oldErrors =
+ oldEntry.getValueInLibrary(descriptor, _librarySource);
+ List<AnalysisError> errors = _updateErrors(oldErrors, newErrors);
+ oldEntry.setValueInLibrary(descriptor, _librarySource, errors);
+ }
+
void _verify(AstNode node) {
LoggingTimer timer = logger.startTimer();
try {
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 422a8e2..c01e769 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -5,9 +5,6 @@
library engine.resolver;
import 'dart:collection';
-import "dart:math" as math;
-
-import 'package:analyzer/src/generated/utilities_collection.dart';
import 'ast.dart';
import 'constant.dart';
@@ -2506,7 +2503,6 @@
interfaceType.typeArguments = typeArguments;
element.type = interfaceType;
// set default constructor
- element.constructors = _createDefaultConstructors(interfaceType);
for (FunctionTypeImpl functionType in _functionTypesToFix) {
functionType.typeArguments = typeArguments;
}
@@ -2631,6 +2627,11 @@
enumElement.enum2 = true;
InterfaceTypeImpl enumType = new InterfaceTypeImpl(enumElement);
enumElement.type = enumType;
+ // The equivalent code for enums in the spec shows a single constructor,
+ // but that constructor is not callable (since it is a compile-time error
+ // to subclass, mix-in, implement, or explicitly instantiate an enum). So
+ // we represent this as having no constructors.
+ enumElement.constructors = ConstructorElement.EMPTY_LIST;
_currentHolder.addEnum(enumElement);
enumName.staticElement = enumElement;
return super.visitEnumDeclaration(node);
@@ -5145,275 +5146,6 @@
}
/**
- * Instances of the class `ImplicitConstructorBuilder` are used to build
- * implicit constructors for mixin applications, and to check for errors
- * related to super constructor calls in class declarations with mixins.
- *
- * The visitor methods don't directly build the implicit constructors or check
- * for errors, since they don't in general visit the classes in the proper
- * order to do so correctly. Instead, they pass closures to
- * ImplicitConstructorBuilderCallback to inform it of the computations to be
- * done and their ordering dependencies.
- */
-class ImplicitConstructorBuilder extends SimpleElementVisitor {
- final AnalysisErrorListener errorListener;
-
- /**
- * Callback to receive the computations to be performed.
- */
- final ImplicitConstructorBuilderCallback _callback;
-
- /**
- * Initialize a newly created visitor to build implicit constructors.
- *
- * The visit methods will pass closures to [_callback] to indicate what
- * computation needs to be performed, and its dependency order.
- */
- ImplicitConstructorBuilder(this.errorListener, this._callback);
-
- @override
- void visitClassElement(ClassElement classElement) {
- (classElement as ClassElementImpl).mixinErrorsReported = false;
- if (classElement.isMixinApplication) {
- _visitClassTypeAlias(classElement);
- } else {
- _visitClassDeclaration(classElement);
- }
- }
-
- @override
- void visitCompilationUnitElement(CompilationUnitElement element) {
- element.types.forEach(visitClassElement);
- }
-
- @override
- void visitLibraryElement(LibraryElement element) {
- element.units.forEach(visitCompilationUnitElement);
- }
-
- /**
- * Create an implicit constructor that is copied from the given constructor, but that is in the
- * given class.
- *
- * @param classType the class in which the implicit constructor is defined
- * @param explicitConstructor the constructor on which the implicit constructor is modeled
- * @param parameterTypes the types to be replaced when creating parameters
- * @param argumentTypes the types with which the parameters are to be replaced
- * @return the implicit constructor that was created
- */
- ConstructorElement _createImplicitContructor(InterfaceType classType,
- ConstructorElement explicitConstructor, List<DartType> parameterTypes,
- List<DartType> argumentTypes) {
- ConstructorElementImpl implicitConstructor =
- new ConstructorElementImpl(explicitConstructor.name, -1);
- implicitConstructor.synthetic = true;
- implicitConstructor.redirectedConstructor = explicitConstructor;
- implicitConstructor.const2 = explicitConstructor.isConst;
- implicitConstructor.returnType = classType;
- List<ParameterElement> explicitParameters = explicitConstructor.parameters;
- int count = explicitParameters.length;
- if (count > 0) {
- List<ParameterElement> implicitParameters =
- new List<ParameterElement>(count);
- for (int i = 0; i < count; i++) {
- ParameterElement explicitParameter = explicitParameters[i];
- ParameterElementImpl implicitParameter =
- new ParameterElementImpl(explicitParameter.name, -1);
- implicitParameter.const3 = explicitParameter.isConst;
- implicitParameter.final2 = explicitParameter.isFinal;
- implicitParameter.parameterKind = explicitParameter.parameterKind;
- implicitParameter.synthetic = true;
- implicitParameter.type =
- explicitParameter.type.substitute2(argumentTypes, parameterTypes);
- implicitParameters[i] = implicitParameter;
- }
- implicitConstructor.parameters = implicitParameters;
- }
- FunctionTypeImpl type = new FunctionTypeImpl(implicitConstructor);
- type.typeArguments = classType.typeArguments;
- implicitConstructor.type = type;
- return implicitConstructor;
- }
-
- /**
- * Find all the constructors that should be forwarded from the given
- * [superType], to the class or mixin application [classElement],
- * and pass information about them to [callback].
- *
- * Return true if some constructors were considered. (A false return value
- * can only happen if the supeclass is a built-in type, in which case it
- * can't be used as a mixin anyway).
- */
- bool _findForwardedConstructors(ClassElementImpl classElement,
- InterfaceType superType, void callback(
- ConstructorElement explicitConstructor, List<DartType> parameterTypes,
- List<DartType> argumentTypes)) {
- ClassElement superclassElement = superType.element;
- List<ConstructorElement> constructors = superclassElement.constructors;
- int count = constructors.length;
- if (count == 0) {
- return false;
- }
- List<DartType> parameterTypes =
- TypeParameterTypeImpl.getTypes(superType.typeParameters);
- List<DartType> argumentTypes = _getArgumentTypes(superType, parameterTypes);
- for (int i = 0; i < count; i++) {
- ConstructorElement explicitConstructor = constructors[i];
- if (!explicitConstructor.isFactory &&
- classElement.isSuperConstructorAccessible(explicitConstructor)) {
- callback(explicitConstructor, parameterTypes, argumentTypes);
- }
- }
- return true;
- }
-
- /**
- * Return a list of argument types that corresponds to the [parameterTypes]
- * and that are derived from the type arguments of the given [superType].
- */
- List<DartType> _getArgumentTypes(
- InterfaceType superType, List<DartType> parameterTypes) {
- DynamicTypeImpl dynamic = DynamicTypeImpl.instance;
- int parameterCount = parameterTypes.length;
- List<DartType> types = new List<DartType>(parameterCount);
- if (superType == null) {
- types = new List<DartType>.filled(parameterCount, dynamic);
- } else {
- List<DartType> typeArguments = superType.typeArguments;
- int argumentCount = math.min(typeArguments.length, parameterCount);
- for (int i = 0; i < argumentCount; i++) {
- types[i] = typeArguments[i];
- }
- for (int i = argumentCount; i < parameterCount; i++) {
- types[i] = dynamic;
- }
- }
- return types;
- }
-
- void _visitClassDeclaration(ClassElementImpl classElement) {
- DartType superType = classElement.supertype;
- if (superType != null && classElement.mixins.isNotEmpty) {
- // We don't need to build any implicitly constructors for the mixin
- // application (since there isn't an explicit element for it), but we
- // need to verify that they _could_ be built.
- if (superType is! InterfaceType) {
- TypeProvider typeProvider = classElement.context.typeProvider;
- superType = typeProvider.objectType;
- }
- ClassElement superElement = superType.element;
- if (superElement != null) {
- _callback(classElement, superElement, () {
- bool constructorFound = false;
- void callback(ConstructorElement explicitConstructor,
- List<DartType> parameterTypes, List<DartType> argumentTypes) {
- constructorFound = true;
- }
- if (_findForwardedConstructors(classElement, superType, callback) &&
- !constructorFound) {
- SourceRange withRange = classElement.withClauseRange;
- errorListener.onError(new AnalysisError(classElement.source,
- withRange.offset, withRange.length,
- CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
- [superElement.name]));
- classElement.mixinErrorsReported = true;
- }
- });
- }
- }
- }
-
- void _visitClassTypeAlias(ClassElementImpl classElement) {
- InterfaceType superType = classElement.supertype;
- if (superType is InterfaceType) {
- ClassElement superElement = superType.element;
- _callback(classElement, superElement, () {
- List<ConstructorElement> implicitConstructors =
- new List<ConstructorElement>();
- void callback(ConstructorElement explicitConstructor,
- List<DartType> parameterTypes, List<DartType> argumentTypes) {
- implicitConstructors.add(_createImplicitContructor(classElement.type,
- explicitConstructor, parameterTypes, argumentTypes));
- }
- if (_findForwardedConstructors(classElement, superType, callback)) {
- if (implicitConstructors.isEmpty) {
- errorListener.onError(new AnalysisError(classElement.source,
- classElement.nameOffset, classElement.name.length,
- CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
- [superElement.name]));
- } else {
- classElement.constructors = implicitConstructors;
- }
- }
- });
- }
- }
-}
-
-/**
- * An instance of this class is capable of running ImplicitConstructorBuilder
- * over all classes in a library cycle.
- */
-class ImplicitConstructorComputer {
- /**
- * Directed graph of dependencies between classes that need to have their
- * implicit constructors computed. Each edge in the graph points from a
- * derived class to its superclass. Implicit constructors will be computed
- * for the superclass before they are compute for the derived class.
- */
- DirectedGraph<ClassElement> _dependencies = new DirectedGraph<ClassElement>();
-
- /**
- * Map from ClassElement to the function which will compute the class's
- * implicit constructors.
- */
- Map<ClassElement, VoidFunction> _computations =
- new HashMap<ClassElement, VoidFunction>();
-
- /**
- * Add the given [libraryElement] to the list of libraries which need to have
- * implicit constructors built for them.
- */
- void add(AnalysisErrorListener errorListener, LibraryElement libraryElement) {
- libraryElement
- .accept(new ImplicitConstructorBuilder(errorListener, _defer));
- }
-
- /**
- * Compute the implicit constructors for all compilation units that have been
- * passed to [add].
- */
- void compute() {
- List<List<ClassElement>> topologicalSort =
- _dependencies.computeTopologicalSort();
- for (List<ClassElement> classesInCycle in topologicalSort) {
- // Note: a cycle could occur if there is a loop in the inheritance graph.
- // Such loops are forbidden by Dart but could occur in the analysis of
- // incorrect code. If this happens, we simply visit the classes
- // constituting the loop in any order.
- for (ClassElement classElement in classesInCycle) {
- VoidFunction computation = _computations[classElement];
- if (computation != null) {
- computation();
- }
- }
- }
- }
-
- /**
- * Defer execution of [computation], which builds implicit constructors for
- * [classElement], until after implicit constructors have been built for
- * [superclassElement].
- */
- void _defer(ClassElement classElement, ClassElement superclassElement,
- void computation()) {
- assert(!_computations.containsKey(classElement));
- _computations[classElement] = computation;
- _dependencies.addEdge(classElement, superclassElement);
- }
-}
-
-/**
* Instances of the class `ImplicitLabelScope` represent the scope statements
* that can be the target of unlabeled break and continue statements.
*/
@@ -6037,9 +5769,14 @@
String key = map.getKey(j);
ExecutableElement value = map.getValue(j);
if (key != null) {
- if (resultMap.get(key) == null ||
- (resultMap.get(key) != null && !_isAbstract(value))) {
- resultMap.put(key, value);
+ ClassElement definingClass = value
+ .getAncestor((Element element) => element is ClassElement);
+ if (!definingClass.type.isObject) {
+ ExecutableElement existingValue = resultMap.get(key);
+ if (existingValue == null ||
+ (existingValue != null && !_isAbstract(value))) {
+ resultMap.put(key, value);
+ }
}
}
}
@@ -6872,7 +6609,7 @@
/**
* The listener to which analysis errors will be reported.
*/
- final AnalysisErrorListener _errorListener;
+ final AnalysisErrorListener errorListener;
/**
* The source specifying the defining compilation unit of this library.
@@ -6924,7 +6661,7 @@
* @param errorListener the listener to which analysis errors will be reported
* @param librarySource the source specifying the defining compilation unit of this library
*/
- Library(this._analysisContext, this._errorListener, this.librarySource) {
+ Library(this._analysisContext, this.errorListener, this.librarySource) {
this._libraryElement =
_analysisContext.getLibraryElement(librarySource) as LibraryElementImpl;
}
@@ -7063,7 +6800,7 @@
*/
LibraryScope get libraryScope {
if (_libraryScope == null) {
- _libraryScope = new LibraryScope(_libraryElement, _errorListener);
+ _libraryScope = new LibraryScope(_libraryElement, errorListener);
}
return _libraryScope;
}
@@ -7094,7 +6831,7 @@
Source getSource(UriBasedDirective directive) {
StringLiteral uriLiteral = directive.uri;
if (uriLiteral is StringInterpolation) {
- _errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset,
+ errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset,
uriLiteral.length, CompileTimeErrorCode.URI_WITH_INTERPOLATION));
return null;
}
@@ -7111,13 +6848,13 @@
Source source =
_analysisContext.sourceFactory.resolveUri(librarySource, uriContent);
if (!_analysisContext.exists(source)) {
- _errorListener.onError(new AnalysisError(librarySource,
+ errorListener.onError(new AnalysisError(librarySource,
uriLiteral.offset, uriLiteral.length,
CompileTimeErrorCode.URI_DOES_NOT_EXIST, [uriContent]));
}
return source;
} on URISyntaxException {
- _errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset,
+ errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset,
uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriContent]));
}
return null;
@@ -7917,7 +7654,6 @@
_typeProvider = new TypeProviderImpl(coreElement, asyncElement);
_buildEnumMembers();
_buildTypeHierarchies();
- _buildImplicitConstructors();
//
// Perform resolution and type analysis.
//
@@ -8198,22 +7934,6 @@
}
/**
- * Finish steps that the [buildTypeHierarchies] could not perform, see
- * [ImplicitConstructorBuilder].
- *
- * @throws AnalysisException if any of the type hierarchies could not be resolved
- */
- void _buildImplicitConstructors() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- ImplicitConstructorComputer computer = new ImplicitConstructorComputer();
- for (Library library in _librariesInCycles) {
- computer.add(_errorListener, library.libraryElement);
- }
- computer.compute();
- });
- }
-
- /**
* Resolve the type hierarchy across all of the types declared in the libraries in the current
* cycle.
*
@@ -8226,7 +7946,9 @@
TypeResolverVisitorFactory typeResolverVisitorFactory =
analysisContext.typeResolverVisitorFactory;
TypeResolverVisitor visitor = (typeResolverVisitorFactory == null)
- ? new TypeResolverVisitor.con1(library, source, _typeProvider)
+ ? new TypeResolverVisitor(library.libraryElement, source,
+ _typeProvider, library.errorListener,
+ nameScope: library.libraryScope)
: typeResolverVisitorFactory(library, source, _typeProvider);
library.getAST(source).accept(visitor);
}
@@ -8485,13 +8207,17 @@
PerformanceStatistics.resolve.makeCurrentWhile(() {
for (Source source in library.compilationUnitSources) {
CompilationUnit ast = library.getAST(source);
- ast.accept(
- new VariableResolverVisitor.con1(library, source, _typeProvider));
+ ast.accept(new VariableResolverVisitor(library.libraryElement, source,
+ _typeProvider, library.errorListener,
+ nameScope: library.libraryScope));
ResolverVisitorFactory visitorFactory =
analysisContext.resolverVisitorFactory;
ResolverVisitor visitor = visitorFactory != null
? visitorFactory(library, source, _typeProvider)
- : new ResolverVisitor.con1(library, source, _typeProvider);
+ : new ResolverVisitor(library.libraryElement, source, _typeProvider,
+ library.errorListener,
+ nameScope: library.libraryScope,
+ inheritanceManager: library.inheritanceManager);
ast.accept(visitor);
}
});
@@ -8653,7 +8379,6 @@
_typeProvider = new TypeProviderImpl(coreElement, asyncElement);
_buildEnumMembers();
_buildTypeHierarchies();
- _buildImplicitConstructors();
//
// Perform resolution and type analysis.
//
@@ -8855,22 +8580,6 @@
});
}
- /**
- * Finish steps that the [buildTypeHierarchies] could not perform, see
- * [ImplicitConstructorBuilder].
- *
- * @throws AnalysisException if any of the type hierarchies could not be resolved
- */
- void _buildImplicitConstructors() {
- PerformanceStatistics.resolve.makeCurrentWhile(() {
- ImplicitConstructorComputer computer = new ImplicitConstructorComputer();
- for (ResolvableLibrary library in _librariesInCycle) {
- computer.add(_errorListener, library.libraryElement);
- }
- computer.compute();
- });
- }
-
HashMap<Source, ResolvableLibrary> _buildLibraryMap() {
HashMap<Source, ResolvableLibrary> libraryMap =
new HashMap<Source, ResolvableLibrary>();
@@ -8903,8 +8612,10 @@
in library.resolvableCompilationUnits) {
Source source = unit.source;
CompilationUnit ast = unit.compilationUnit;
- TypeResolverVisitor visitor =
- new TypeResolverVisitor.con4(library, source, _typeProvider);
+ TypeResolverVisitor visitor = new TypeResolverVisitor(
+ library.libraryElement, source, _typeProvider,
+ library.libraryScope.errorListener,
+ nameScope: library.libraryScope);
ast.accept(visitor);
}
}
@@ -8985,10 +8696,13 @@
in library.resolvableCompilationUnits) {
Source source = unit.source;
CompilationUnit ast = unit.compilationUnit;
- ast.accept(
- new VariableResolverVisitor.con3(library, source, _typeProvider));
- ResolverVisitor visitor =
- new ResolverVisitor.con4(library, source, _typeProvider);
+ ast.accept(new VariableResolverVisitor(library.libraryElement, source,
+ _typeProvider, library.libraryScope.errorListener,
+ nameScope: library.libraryScope));
+ ResolverVisitor visitor = new ResolverVisitor(library.libraryElement,
+ source, _typeProvider, library._libraryScope.errorListener,
+ nameScope: library._libraryScope,
+ inheritanceManager: library.inheritanceManager);
ast.accept(visitor);
}
});
@@ -10273,63 +9987,41 @@
bool resolveOnlyCommentInFunctionBody = false;
/**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- */
- ResolverVisitor.con1(
- Library library, Source source, TypeProvider typeProvider,
- {StaticTypeAnalyzer typeAnalyzer,
- StaticTypeAnalyzerFactory typeAnalyzerFactory})
- : super.con1(library, source, typeProvider) {
- this._inheritanceManager = library.inheritanceManager;
- this.elementResolver = new ElementResolver(this);
- this.typeAnalyzer = typeAnalyzer != null
- ? typeAnalyzer
- : (typeAnalyzerFactory != null
- ? typeAnalyzerFactory(this)
- : new StaticTypeAnalyzer(this));
- }
-
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param definingLibrary the element for the library containing the compilation unit being
- * visited
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- * @param errorListener the error listener that will be informed of any errors that are found
- * during resolution
- */
- ResolverVisitor.con2(LibraryElement definingLibrary, Source source,
- TypeProvider typeProvider, InheritanceManager inheritanceManager,
- AnalysisErrorListener errorListener)
- : super.con2(definingLibrary, source, typeProvider, errorListener) {
- this._inheritanceManager = inheritanceManager;
- this.elementResolver = new ElementResolver(this);
- this.typeAnalyzer = new StaticTypeAnalyzer(this);
- }
-
- /**
* Initialize a newly created visitor to resolve the nodes in an AST node.
*
- * @param definingLibrary the element for the library containing the node being visited
- * @param source the source representing the compilation unit containing the node being visited
- * @param typeProvider the object used to access the types from the core library
- * @param nameScope the scope used to resolve identifiers in the node that will first be visited
- * @param errorListener the error listener that will be informed of any errors that are found
- * during resolution
+ * [definingLibrary] is the element for the library containing the node being
+ * visited.
+ * [source] is the source representing the compilation unit containing the
+ * node being visited.
+ * [typeProvider] the object used to access the types from the core library.
+ * [errorListener] the error listener that will be informed of any errors
+ * that are found during resolution.
+ * [nameScope] is the scope used to resolve identifiers in the node that will
+ * first be visited. If `null` or unspecified, a new [LibraryScope] will be
+ * created based on [definingLibrary] and [typeProvider].
+ * [inheritanceManager] is used to perform inheritance lookups. If `null` or
+ * unspecified, a new [InheritanceManager] will be created based on
+ * [definingLibrary].
+ * [typeAnalyzerFactory] is used to create the type analyzer. If `null` or
+ * unspecified, a type analyzer of type [StaticTypeAnalyzer] will be created.
*/
- ResolverVisitor.con3(LibraryElement definingLibrary, Source source,
- TypeProvider typeProvider, Scope nameScope,
- AnalysisErrorListener errorListener)
- : super.con3(
- definingLibrary, source, typeProvider, nameScope, errorListener) {
- this._inheritanceManager = new InheritanceManager(definingLibrary);
+ ResolverVisitor(LibraryElement definingLibrary, Source source,
+ TypeProvider typeProvider, AnalysisErrorListener errorListener,
+ {Scope nameScope, InheritanceManager inheritanceManager,
+ StaticTypeAnalyzerFactory typeAnalyzerFactory})
+ : super(definingLibrary, source, typeProvider, errorListener,
+ nameScope: nameScope) {
+ if (inheritanceManager == null) {
+ this._inheritanceManager = new InheritanceManager(definingLibrary);
+ } else {
+ this._inheritanceManager = inheritanceManager;
+ }
this.elementResolver = new ElementResolver(this);
- this.typeAnalyzer = new StaticTypeAnalyzer(this);
+ if (typeAnalyzerFactory == null) {
+ this.typeAnalyzer = new StaticTypeAnalyzer(this);
+ } else {
+ this.typeAnalyzer = typeAnalyzerFactory(this);
+ }
}
/**
@@ -10338,14 +10030,18 @@
* @param library the library containing the compilation unit being resolved
* @param source the source representing the compilation unit being visited
* @param typeProvider the object used to access the types from the core library
+ *
+ * Deprecated. Please use unnamed constructor instead.
*/
- ResolverVisitor.con4(
- ResolvableLibrary library, Source source, TypeProvider typeProvider)
- : super.con4(library, source, typeProvider) {
- this._inheritanceManager = library.inheritanceManager;
- this.elementResolver = new ElementResolver(this);
- this.typeAnalyzer = new StaticTypeAnalyzer(this);
- }
+ @deprecated
+ ResolverVisitor.con1(
+ Library library, Source source, TypeProvider typeProvider,
+ {StaticTypeAnalyzerFactory typeAnalyzerFactory})
+ : this(
+ library.libraryElement, source, typeProvider, library.errorListener,
+ nameScope: library.libraryScope,
+ inheritanceManager: library.inheritanceManager,
+ typeAnalyzerFactory: typeAnalyzerFactory);
/**
* Return the element representing the function containing the current node, or `null` if
@@ -11966,67 +11662,29 @@
ClassElement enclosingClass;
/**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+ * Initialize a newly created visitor to resolve the nodes in a compilation
+ * unit.
*
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
+ * [definingLibrary] is the element for the library containing the
+ * compilation unit being visited.
+ * [source] is the source representing the compilation unit being visited.
+ * [typeProvider] is the object used to access the types from the core
+ * library.
+ * [errorListener] is the error listener that will be informed of any errors
+ * that are found during resolution.
+ * [nameScope] is the scope used to resolve identifiers in the node that will
+ * first be visited. If `null` or unspecified, a new [LibraryScope] will be
+ * created based on [definingLibrary] and [typeProvider].
*/
- ScopedVisitor.con1(Library library, this.source, this.typeProvider) {
- this._definingLibrary = library.libraryElement;
- LibraryScope libraryScope = library.libraryScope;
- this._errorListener = libraryScope.errorListener;
- this.nameScope = libraryScope;
- }
-
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param definingLibrary the element for the library containing the compilation unit being
- * visited
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- * @param errorListener the error listener that will be informed of any errors that are found
- * during resolution
- */
- ScopedVisitor.con2(LibraryElement definingLibrary, this.source,
- this.typeProvider, AnalysisErrorListener errorListener) {
+ ScopedVisitor(LibraryElement definingLibrary, this.source, this.typeProvider,
+ AnalysisErrorListener errorListener, {Scope nameScope}) {
this._definingLibrary = definingLibrary;
this._errorListener = errorListener;
- this.nameScope = new LibraryScope(definingLibrary, errorListener);
- }
-
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param definingLibrary the element for the library containing the compilation unit being
- * visited
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- * @param nameScope the scope used to resolve identifiers in the node that will first be visited
- * @param errorListener the error listener that will be informed of any errors that are found
- * during resolution
- */
- ScopedVisitor.con3(LibraryElement definingLibrary, this.source,
- this.typeProvider, Scope nameScope, AnalysisErrorListener errorListener) {
- this._definingLibrary = definingLibrary;
- this._errorListener = errorListener;
- this.nameScope = nameScope;
- }
-
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- */
- ScopedVisitor.con4(
- ResolvableLibrary library, this.source, this.typeProvider) {
- this._definingLibrary = library.libraryElement;
- LibraryScope libraryScope = library.libraryScope;
- this._errorListener = libraryScope.errorListener;
- this.nameScope = libraryScope;
+ if (nameScope == null) {
+ this.nameScope = new LibraryScope(definingLibrary, errorListener);
+ } else {
+ this.nameScope = nameScope;
+ }
}
/**
@@ -13663,65 +13321,25 @@
bool _hasReferenceToSuper = false;
/**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- */
- TypeResolverVisitor.con1(
- Library library, Source source, TypeProvider typeProvider)
- : super.con1(library, source, typeProvider) {
- _dynamicType = typeProvider.dynamicType;
- _undefinedType = typeProvider.undefinedType;
- }
-
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param definingLibrary the element for the library containing the compilation unit being
- * visited
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- * @param errorListener the error listener that will be informed of any errors that are found
- * during resolution
- */
- TypeResolverVisitor.con2(LibraryElement definingLibrary, Source source,
- TypeProvider typeProvider, AnalysisErrorListener errorListener)
- : super.con2(definingLibrary, source, typeProvider, errorListener) {
- _dynamicType = typeProvider.dynamicType;
- _undefinedType = typeProvider.undefinedType;
- }
-
- /**
* Initialize a newly created visitor to resolve the nodes in an AST node.
*
- * @param definingLibrary the element for the library containing the node being visited
- * @param source the source representing the compilation unit containing the node being visited
- * @param typeProvider the object used to access the types from the core library
- * @param nameScope the scope used to resolve identifiers in the node that will first be visited
- * @param errorListener the error listener that will be informed of any errors that are found
- * during resolution
+ * [definingLibrary] is the element for the library containing the node being
+ * visited.
+ * [source] is the source representing the compilation unit containing the
+ * node being visited.
+ * [typeProvider] is the object used to access the types from the core
+ * library.
+ * [errorListener] is the error listener that will be informed of any errors
+ * that are found during resolution.
+ * [nameScope] is the scope used to resolve identifiers in the node that will
+ * first be visited. If `null` or unspecified, a new [LibraryScope] will be
+ * created based on [definingLibrary] and [typeProvider].
*/
- TypeResolverVisitor.con3(LibraryElement definingLibrary, Source source,
- TypeProvider typeProvider, Scope nameScope,
- AnalysisErrorListener errorListener)
- : super.con3(
- definingLibrary, source, typeProvider, nameScope, errorListener) {
- _dynamicType = typeProvider.dynamicType;
- _undefinedType = typeProvider.undefinedType;
- }
-
- /**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- */
- TypeResolverVisitor.con4(
- ResolvableLibrary library, Source source, TypeProvider typeProvider)
- : super.con4(library, source, typeProvider) {
+ TypeResolverVisitor(LibraryElement definingLibrary, Source source,
+ TypeProvider typeProvider, AnalysisErrorListener errorListener,
+ {Scope nameScope})
+ : super(definingLibrary, source, typeProvider, errorListener,
+ nameScope: nameScope) {
_dynamicType = typeProvider.dynamicType;
_undefinedType = typeProvider.undefinedType;
}
@@ -15231,31 +14849,25 @@
ExecutableElement _enclosingFunction;
/**
- * Initialize a newly created visitor to resolve the nodes in a compilation unit.
- *
- * @param library the library containing the compilation unit being resolved
- * @param source the source representing the compilation unit being visited
- * @param typeProvider the object used to access the types from the core library
- */
- VariableResolverVisitor.con1(
- Library library, Source source, TypeProvider typeProvider)
- : super.con1(library, source, typeProvider);
-
- /**
* Initialize a newly created visitor to resolve the nodes in an AST node.
*
- * @param definingLibrary the element for the library containing the node being visited
- * @param source the source representing the compilation unit containing the node being visited
- * @param typeProvider the object used to access the types from the core library
- * @param nameScope the scope used to resolve identifiers in the node that will first be visited
- * @param errorListener the error listener that will be informed of any errors that are found
- * during resolution
+ * [definingLibrary] is the element for the library containing the node being
+ * visited.
+ * [source] is the source representing the compilation unit containing the
+ * node being visited
+ * [typeProvider] is the object used to access the types from the core
+ * library.
+ * [errorListener] is the error listener that will be informed of any errors
+ * that are found during resolution.
+ * [nameScope] is the scope used to resolve identifiers in the node that will
+ * first be visited. If `null` or unspecified, a new [LibraryScope] will be
+ * created based on [definingLibrary] and [typeProvider].
*/
- VariableResolverVisitor.con2(LibraryElement definingLibrary, Source source,
- TypeProvider typeProvider, Scope nameScope,
- AnalysisErrorListener errorListener)
- : super.con3(
- definingLibrary, source, typeProvider, nameScope, errorListener);
+ VariableResolverVisitor(LibraryElement definingLibrary, Source source,
+ TypeProvider typeProvider, AnalysisErrorListener errorListener,
+ {Scope nameScope})
+ : super(definingLibrary, source, typeProvider, errorListener,
+ nameScope: nameScope);
/**
* Initialize a newly created visitor to resolve the nodes in a compilation unit.
@@ -15263,10 +14875,15 @@
* @param library the library containing the compilation unit being resolved
* @param source the source representing the compilation unit being visited
* @param typeProvider the object used to access the types from the core library
+ *
+ * Deprecated. Please use unnamed constructor instead.
*/
- VariableResolverVisitor.con3(
- ResolvableLibrary library, Source source, TypeProvider typeProvider)
- : super.con4(library, source, typeProvider);
+ @deprecated
+ VariableResolverVisitor.con1(
+ Library library, Source source, TypeProvider typeProvider)
+ : this(
+ library.libraryElement, source, typeProvider, library.errorListener,
+ nameScope: library.libraryScope);
@override
Object visitExportDirective(ExportDirective node) => null;
diff --git a/pkg/analyzer/lib/src/generated/source.dart b/pkg/analyzer/lib/src/generated/source.dart
index 4900cf6..75c175a 100644
--- a/pkg/analyzer/lib/src/generated/source.dart
+++ b/pkg/analyzer/lib/src/generated/source.dart
@@ -11,8 +11,11 @@
import "dart:math" as math;
import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/source/package_map_resolver.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart' as utils;
import 'package:analyzer/task/model.dart';
+import 'package:package_config/packages.dart';
import 'package:path/path.dart' as pathos;
import 'engine.dart';
@@ -575,6 +578,17 @@
AnalysisContext context;
/**
+ * URI processor used to find mappings for `package:` URIs found in a `.packages` config
+ * file.
+ */
+ final Packages _packages;
+
+ /**
+ * Resource provider used in working with package maps.
+ */
+ final ResourceProvider _resourceProvider;
+
+ /**
* The resolvers used to resolve absolute URI's.
*/
final List<UriResolver> _resolvers;
@@ -585,11 +599,14 @@
LocalSourcePredicate _localSourcePredicate = LocalSourcePredicate.NOT_SDK;
/**
- * Initialize a newly created source factory.
- *
- * @param resolvers the resolvers used to resolve absolute URI's
+ * Initialize a newly created source factory with the given absolute URI [resolvers] and
+ * optional [packages] resolution helper.
*/
- SourceFactory(this._resolvers);
+ SourceFactory(this._resolvers,
+ [this._packages, ResourceProvider resourceProvider])
+ : _resourceProvider = resourceProvider != null
+ ? resourceProvider
+ : PhysicalResourceProvider.INSTANCE;
/**
* Return the [DartSdk] associated with this [SourceFactory], or `null` if there
@@ -620,6 +637,19 @@
/// A table mapping package names to paths of directories containing
/// the package (or [null] if there is no registered package URI resolver).
Map<String, List<Folder>> get packageMap {
+ // Start by looking in .packages.
+ if (_packages != null) {
+ Map<String, List<Folder>> packageMap = <String, List<Folder>>{};
+ _packages.asMap().forEach((String name, Uri uri) {
+ if (uri.scheme == 'file' || uri.scheme == '' /* unspecified */) {
+ packageMap[name] =
+ <Folder>[_resourceProvider.getFolder(uri.toFilePath())];
+ }
+ });
+ return packageMap;
+ }
+
+ // Default to the PackageMapUriResolver.
PackageMapUriResolver resolver = _resolvers.firstWhere(
(r) => r is PackageMapUriResolver, orElse: () => null);
return resolver != null ? resolver.packageMap : null;
@@ -727,15 +757,44 @@
* @return the absolute URI representing the given source
*/
Uri restoreUri(Source source) {
+ // First see if a resolver can restore the URI.
for (UriResolver resolver in _resolvers) {
Uri uri = resolver.restoreAbsolute(source);
if (uri != null) {
+ // Now see if there's a package mapping.
+ Uri packageMappedUri = _getPackageMapping(uri);
+ if (packageMappedUri != null) {
+ return packageMappedUri;
+ }
+ // Fall back to the resolver's computed URI.
return uri;
}
}
+
return null;
}
+ Uri _getPackageMapping(Uri sourceUri) {
+ if (_packages == null) {
+ return null;
+ }
+ if (sourceUri.scheme != 'file') {
+ //TODO(pquitslund): verify this works for non-file URIs.
+ return null;
+ }
+
+ Uri packageUri;
+ _packages.asMap().forEach((String name, Uri uri) {
+ if (packageUri == null) {
+ if (utils.startsWith(sourceUri, uri)) {
+ packageUri = Uri.parse(
+ 'package:$name/${sourceUri.path.substring(uri.path.length)}');
+ }
+ }
+ });
+ return packageUri;
+ }
+
/**
* Return a source object representing the URI that results from resolving the given (possibly
* relative) contained URI against the URI associated with an existing source object, or
@@ -755,6 +814,15 @@
}
containedUri = containingSource.resolveRelativeUri(containedUri);
}
+ // Now check .packages.
+ if (_packages != null && containedUri.scheme == 'package') {
+ Uri packageUri =
+ _packages.resolve(containedUri, notFound: (Uri packageUri) => null);
+ //TODO(pquitslund): package_config needs to be updated to set schemes for file URIs.
+ if (packageUri != null && packageUri.scheme == '') {
+ containedUri = packageUri.replace(scheme: 'file');
+ }
+ }
for (UriResolver resolver in _resolvers) {
Source result = resolver.resolveAbsolute(containedUri);
if (result != null) {
diff --git a/pkg/analyzer/lib/src/generated/source_io.dart b/pkg/analyzer/lib/src/generated/source_io.dart
index 1c60f5d..9c40d74 100644
--- a/pkg/analyzer/lib/src/generated/source_io.dart
+++ b/pkg/analyzer/lib/src/generated/source_io.dart
@@ -283,6 +283,14 @@
return new FileBasedSource(new JavaFile.fromUri(uri), uri);
}
+ @override
+ Uri restoreAbsolute(Source source) {
+ if (source is FileBasedSource) {
+ return new Uri.file(source.fullName);
+ }
+ return null;
+ }
+
/**
* Return `true` if the given URI is a `file` URI.
*
diff --git a/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart b/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart
index cc90ec6..217a0d8 100644
--- a/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart
+++ b/pkg/analyzer/lib/src/generated/testing/test_type_provider.dart
@@ -268,6 +268,7 @@
"iterator", false, iteratorType.substitute4(<DartType>[eType])),
ElementFactory.getterElement("last", false, eType)
]);
+ iterableElement.constructors = ConstructorElement.EMPTY_LIST;
_propagateTypeArguments(iterableElement);
}
return _iterableType;
@@ -282,6 +283,7 @@
_setAccessors(iteratorElement, <PropertyAccessorElement>[
ElementFactory.getterElement("current", false, eType)
]);
+ iteratorElement.constructors = ConstructorElement.EMPTY_LIST;
_propagateTypeArguments(iteratorElement);
}
return _iteratorType;
@@ -330,6 +332,7 @@
ElementFactory.methodElement(
"[]=", VoidTypeImpl.instance, [kType, vType])
];
+ mapElement.constructors = ConstructorElement.EMPTY_LIST;
_propagateTypeArguments(mapElement);
}
return _mapType;
@@ -356,7 +359,9 @@
@override
InterfaceType get nullType {
if (_nullType == null) {
- _nullType = ElementFactory.classElement2("Null").type;
+ ClassElementImpl nullElement = ElementFactory.classElement2("Null");
+ nullElement.constructors = ConstructorElement.EMPTY_LIST;
+ _nullType = nullElement.type;
}
return _nullType;
}
@@ -552,7 +557,9 @@
];
fromEnvironment.factory = true;
fromEnvironment.isCycleFree = true;
+ numElement.constructors = ConstructorElement.EMPTY_LIST;
intElement.constructors = <ConstructorElement>[fromEnvironment];
+ doubleElement.constructors = ConstructorElement.EMPTY_LIST;
List<FieldElement> fields = <FieldElement>[
ElementFactory.fieldElement("NAN", true, false, true, _doubleType),
ElementFactory.fieldElement("INFINITY", true, false, true, _doubleType),
diff --git a/pkg/analyzer/lib/src/generated/utilities_dart.dart b/pkg/analyzer/lib/src/generated/utilities_dart.dart
index 26456a5..cd9753c 100644
--- a/pkg/analyzer/lib/src/generated/utilities_dart.dart
+++ b/pkg/analyzer/lib/src/generated/utilities_dart.dart
@@ -38,3 +38,27 @@
const ParameterKind(String name, int ordinal, this.isOptional)
: super(name, ordinal);
}
+
+/**
+ * Check whether [uri1] starts with (or 'is prefixed by') [uri2] by checking
+ * path segments.
+ */
+bool startsWith(Uri uri1, Uri uri2) {
+ List<String> uri1Segments = uri1.pathSegments;
+ List<String> uri2Segments = uri2.pathSegments.toList();
+ // Trim trailing empty segments ('/foo/' => ['foo', ''])
+ if (uri2Segments.last == '') {
+ uri2Segments.removeLast();
+ }
+
+ if (uri2Segments.length > uri1Segments.length) {
+ return false;
+ }
+
+ for (int i = 0; i < uri2Segments.length; ++i) {
+ if (uri2Segments[i] != uri1Segments[i]) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/pkg/analyzer/lib/src/plugin/engine_plugin.dart b/pkg/analyzer/lib/src/plugin/engine_plugin.dart
index 6269cf7..eb516de 100644
--- a/pkg/analyzer/lib/src/plugin/engine_plugin.dart
+++ b/pkg/analyzer/lib/src/plugin/engine_plugin.dart
@@ -62,12 +62,10 @@
//
// Register Dart tasks.
//
- registerExtension(taskId, BuildClassConstructorsTask.DESCRIPTOR);
registerExtension(taskId, BuildCompilationUnitElementTask.DESCRIPTOR);
registerExtension(taskId, BuildDirectiveElementsTask.DESCRIPTOR);
registerExtension(taskId, BuildEnumMemberElementsTask.DESCRIPTOR);
registerExtension(taskId, BuildExportNamespaceTask.DESCRIPTOR);
- registerExtension(taskId, BuildLibraryConstructorsTask.DESCRIPTOR);
registerExtension(taskId, BuildLibraryElementTask.DESCRIPTOR);
registerExtension(taskId, BuildPublicNamespaceTask.DESCRIPTOR);
registerExtension(taskId, BuildSourceExportClosureTask.DESCRIPTOR);
diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart
index 849ae33..ff5b0cb 100644
--- a/pkg/analyzer/lib/src/task/dart.dart
+++ b/pkg/analyzer/lib/src/task/dart.dart
@@ -5,7 +5,6 @@
library analyzer.src.task.dart;
import 'dart:collection';
-import 'dart:math' as math;
import 'package:analyzer/src/context/cache.dart';
import 'package:analyzer/src/generated/ast.dart';
@@ -59,17 +58,6 @@
'BUILD_LIBRARY_ERRORS', AnalysisError.NO_ERRORS);
/**
- * The [ClassElement]s of a [Source] representing a Dart library.
- *
- * The list contains the elements for all of the classes defined in the library,
- * not just those in the defining compilation unit. The list will be empty if
- * there are no classes, but will not be `null`.
- */
-final ListResultDescriptor<ClassElement> CLASS_ELEMENTS =
- new ListResultDescriptor<ClassElement>('CLASS_ELEMENTS', null,
- cachingPolicy: ELEMENT_CACHING_POLICY);
-
-/**
* A list of the [ConstantEvaluationTarget]s defined in a unit. This includes
* constants defined at top level, statically inside classes, and local to
* functions, as well as constant constructors, annotations, and default values
@@ -112,23 +100,6 @@
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
- * The [ConstructorElement]s of a [ClassElement].
- */
-final ListResultDescriptor<ConstructorElement> CONSTRUCTORS =
- new ListResultDescriptor<ConstructorElement>('CONSTRUCTORS', null);
-
-/**
- * The errors produced while building a [ClassElement] constructors.
- *
- * The list will be empty if there were no errors, but will not be `null`.
- *
- * The result is only available for targets representing a [ClassElement].
- */
-final ListResultDescriptor<AnalysisError> CONSTRUCTORS_ERRORS =
- new ListResultDescriptor<AnalysisError>(
- 'CONSTRUCTORS_ERRORS', AnalysisError.NO_ERRORS);
-
-/**
* The sources representing the libraries that include a given source as a part.
*
* The result is only available for [Source]s representing a compilation unit.
@@ -233,17 +204,6 @@
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
- * The partial [LibraryElement] associated with a library.
- *
- * [LIBRARY_ELEMENT5] plus resolved elements and types for all expressions.
- *
- * The result is only available for [Source]s representing a library.
- */
-final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT6 =
- new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT6', null,
- cachingPolicy: ELEMENT_CACHING_POLICY);
-
-/**
* The flag specifying whether all analysis errors are computed in a specific
* library.
*
@@ -427,234 +387,6 @@
}
/**
- * A task that builds implicit constructors for a [ClassElement], or keeps
- * the existing explicit constructors if the class has them.
- */
-class BuildClassConstructorsTask extends SourceBasedAnalysisTask {
- /**
- * The name of the [CONSTRUCTORS] input for the superclass.
- */
- static const String SUPER_CONSTRUCTORS = 'SUPER_CONSTRUCTORS';
-
- /**
- * The task descriptor describing this kind of task.
- */
- static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
- 'BuildConstructorsForClassTask', createTask, buildInputs,
- <ResultDescriptor>[CONSTRUCTORS, CONSTRUCTORS_ERRORS]);
-
- BuildClassConstructorsTask(
- InternalAnalysisContext context, AnalysisTarget target)
- : super(context, target);
-
- @override
- TaskDescriptor get descriptor => DESCRIPTOR;
-
- @override
- void internalPerform() {
- List<AnalysisError> errors = <AnalysisError>[];
- //
- // Prepare inputs.
- //
- ClassElementImpl classElement = this.target;
- List<ConstructorElement> superConstructors = inputs[SUPER_CONSTRUCTORS];
- DartType superType = classElement.supertype;
- if (superType == null) {
- return;
- }
- //
- // Shortcut for ClassElement(s) without implicit constructors.
- //
- if (superConstructors == null) {
- outputs[CONSTRUCTORS] = classElement.constructors;
- outputs[CONSTRUCTORS_ERRORS] = AnalysisError.NO_ERRORS;
- return;
- }
- //
- // ClassTypeAlias
- //
- if (classElement.isMixinApplication) {
- List<ConstructorElement> implicitConstructors =
- new List<ConstructorElement>();
- void callback(ConstructorElement explicitConstructor,
- List<DartType> parameterTypes, List<DartType> argumentTypes) {
- implicitConstructors.add(_createImplicitContructor(classElement.type,
- explicitConstructor, parameterTypes, argumentTypes));
- }
- if (_findForwardedConstructors(classElement, superType, callback)) {
- if (implicitConstructors.isEmpty) {
- errors.add(new AnalysisError(classElement.source,
- classElement.nameOffset, classElement.name.length,
- CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
- [superType.element.name]));
- } else {
- classElement.constructors = implicitConstructors;
- }
- }
- outputs[CONSTRUCTORS] = classElement.constructors;
- outputs[CONSTRUCTORS_ERRORS] = errors;
- }
- //
- // ClassDeclaration
- //
- if (!classElement.isMixinApplication) {
- bool constructorFound = false;
- void callback(ConstructorElement explicitConstructor,
- List<DartType> parameterTypes, List<DartType> argumentTypes) {
- constructorFound = true;
- }
- if (_findForwardedConstructors(classElement, superType, callback) &&
- !constructorFound) {
- SourceRange withRange = classElement.withClauseRange;
- errors.add(new AnalysisError(classElement.source, withRange.offset,
- withRange.length, CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
- [superType.element.name]));
- classElement.mixinErrorsReported = true;
- }
- outputs[CONSTRUCTORS] = classElement.constructors;
- outputs[CONSTRUCTORS_ERRORS] = errors;
- }
- }
-
- /**
- * Return a map from the names of the inputs of this kind of task to the task
- * input descriptors describing those inputs for a task with the
- * given [classElement].
- */
- static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
- ClassElement element = target;
- Source librarySource = element.library.source;
- DartType superType = element.supertype;
- if (superType is InterfaceType) {
- if (element.isMixinApplication || element.mixins.isNotEmpty) {
- ClassElement superElement = superType.element;
- return <String, TaskInput>{
- 'libraryDep': LIBRARY_ELEMENT5.of(librarySource),
- SUPER_CONSTRUCTORS: CONSTRUCTORS.of(superElement)
- };
- }
- }
- // No implicit constructors.
- // Depend on LIBRARY_ELEMENT5 for invalidation.
- return <String, TaskInput>{
- 'libraryDep': LIBRARY_ELEMENT5.of(librarySource)
- };
- }
-
- /**
- * Create a [BuildClassConstructorsTask] based on the given
- * [target] in the given [context].
- */
- static BuildClassConstructorsTask createTask(
- AnalysisContext context, AnalysisTarget target) {
- return new BuildClassConstructorsTask(context, target);
- }
-
- /**
- * Create an implicit constructor that is copied from the given
- * [explicitConstructor], but that is in the given class.
- *
- * [classType] - the class in which the implicit constructor is defined.
- * [explicitConstructor] - the constructor on which the implicit constructor
- * is modeled.
- * [parameterTypes] - the types to be replaced when creating parameters.
- * [argumentTypes] - the types with which the parameters are to be replaced.
- */
- static ConstructorElement _createImplicitContructor(InterfaceType classType,
- ConstructorElement explicitConstructor, List<DartType> parameterTypes,
- List<DartType> argumentTypes) {
- ConstructorElementImpl implicitConstructor =
- new ConstructorElementImpl(explicitConstructor.name, -1);
- implicitConstructor.synthetic = true;
- implicitConstructor.redirectedConstructor = explicitConstructor;
- implicitConstructor.const2 = explicitConstructor.isConst;
- implicitConstructor.returnType = classType;
- List<ParameterElement> explicitParameters = explicitConstructor.parameters;
- int count = explicitParameters.length;
- if (count > 0) {
- List<ParameterElement> implicitParameters =
- new List<ParameterElement>(count);
- for (int i = 0; i < count; i++) {
- ParameterElement explicitParameter = explicitParameters[i];
- ParameterElementImpl implicitParameter =
- new ParameterElementImpl(explicitParameter.name, -1);
- implicitParameter.const3 = explicitParameter.isConst;
- implicitParameter.final2 = explicitParameter.isFinal;
- implicitParameter.parameterKind = explicitParameter.parameterKind;
- implicitParameter.synthetic = true;
- implicitParameter.type =
- explicitParameter.type.substitute2(argumentTypes, parameterTypes);
- implicitParameters[i] = implicitParameter;
- }
- implicitConstructor.parameters = implicitParameters;
- }
- FunctionTypeImpl type = new FunctionTypeImpl(implicitConstructor);
- type.typeArguments = classType.typeArguments;
- implicitConstructor.type = type;
- return implicitConstructor;
- }
-
- /**
- * Find all the constructors that should be forwarded from the given
- * [superType], to the class or mixin application [classElement],
- * and pass information about them to [callback].
- *
- * Return `true` if some constructors were considered. (A `false` return value
- * can only happen if the supeclass is a built-in type, in which case it
- * can't be used as a mixin anyway).
- */
- static bool _findForwardedConstructors(ClassElementImpl classElement,
- InterfaceType superType, void callback(
- ConstructorElement explicitConstructor, List<DartType> parameterTypes,
- List<DartType> argumentTypes)) {
- if (superType == null) {
- return false;
- }
- ClassElement superclassElement = superType.element;
- List<ConstructorElement> constructors = superclassElement.constructors;
- int count = constructors.length;
- if (count == 0) {
- return false;
- }
- List<DartType> parameterTypes =
- TypeParameterTypeImpl.getTypes(superType.typeParameters);
- List<DartType> argumentTypes = _getArgumentTypes(superType, parameterTypes);
- for (int i = 0; i < count; i++) {
- ConstructorElement explicitConstructor = constructors[i];
- if (!explicitConstructor.isFactory &&
- classElement.isSuperConstructorAccessible(explicitConstructor)) {
- callback(explicitConstructor, parameterTypes, argumentTypes);
- }
- }
- return true;
- }
-
- /**
- * Return a list of argument types that corresponds to the [parameterTypes]
- * and that are derived from the type arguments of the given [superType].
- */
- static List<DartType> _getArgumentTypes(
- InterfaceType superType, List<DartType> parameterTypes) {
- DynamicTypeImpl dynamic = DynamicTypeImpl.instance;
- int parameterCount = parameterTypes.length;
- List<DartType> types = new List<DartType>(parameterCount);
- if (superType == null) {
- types = new List<DartType>.filled(parameterCount, dynamic);
- } else {
- List<DartType> typeArguments = superType.typeArguments;
- int argumentCount = math.min(typeArguments.length, parameterCount);
- for (int i = 0; i < argumentCount; i++) {
- types[i] = typeArguments[i];
- }
- for (int i = argumentCount; i < parameterCount; i++) {
- types[i] = dynamic;
- }
- }
- return types;
- }
-}
-
-/**
* A task that builds a compilation unit element for a single compilation unit.
*/
class BuildCompilationUnitElementTask extends SourceBasedAnalysisTask {
@@ -1128,59 +860,6 @@
}
/**
- * This task builds [LIBRARY_ELEMENT6] by forcing building constructors for
- * all the classes of the defining and part units of a library.
- */
-class BuildLibraryConstructorsTask extends SourceBasedAnalysisTask {
- /**
- * The name of the [LIBRARY_ELEMENT5] input.
- */
- static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
-
- /**
- * The task descriptor describing this kind of task.
- */
- static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
- 'BuildLibraryConstructorsTask', createTask, buildInputs,
- <ResultDescriptor>[LIBRARY_ELEMENT6]);
-
- BuildLibraryConstructorsTask(
- InternalAnalysisContext context, AnalysisTarget target)
- : super(context, target);
-
- @override
- TaskDescriptor get descriptor => DESCRIPTOR;
-
- @override
- void internalPerform() {
- LibraryElement library = getRequiredInput(LIBRARY_INPUT);
- outputs[LIBRARY_ELEMENT6] = library;
- }
-
- /**
- * Return a map from the names of the inputs of this kind of task to the task
- * input descriptors describing those inputs for a task with the
- * given [target].
- */
- static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
- Source source = target;
- return <String, TaskInput>{
- LIBRARY_INPUT: LIBRARY_ELEMENT5.of(source),
- 'resolvedConstructors': CLASS_ELEMENTS.of(source).toListOf(CONSTRUCTORS),
- };
- }
-
- /**
- * Create a [BuildLibraryConstructorsTask] based on the given [target] in
- * the given [context].
- */
- static BuildLibraryConstructorsTask createTask(
- AnalysisContext context, AnalysisTarget target) {
- return new BuildLibraryConstructorsTask(context, target);
- }
-}
-
-/**
* A task that builds a library element for a Dart library.
*/
class BuildLibraryElementTask extends SourceBasedAnalysisTask {
@@ -1201,7 +880,6 @@
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
'BuildLibraryElementTask', createTask, buildInputs, <ResultDescriptor>[
BUILD_LIBRARY_ERRORS,
- CLASS_ELEMENTS,
LIBRARY_ELEMENT1,
IS_LAUNCHABLE
]);
@@ -1337,17 +1015,9 @@
_patchTopLevelAccessors(libraryElement);
}
//
- // Prepare all class elements.
- //
- List<ClassElement> classElements = libraryElement.units
- .map((CompilationUnitElement unitElement) => unitElement.types)
- .expand((List<ClassElement> unitClassElements) => unitClassElements)
- .toList();
- //
// Record outputs.
//
outputs[BUILD_LIBRARY_ERRORS] = errors;
- outputs[CLASS_ELEMENTS] = classElements;
outputs[LIBRARY_ELEMENT1] = libraryElement;
outputs[IS_LAUNCHABLE] = entryPoint != null;
}
@@ -2614,11 +2284,6 @@
static const String VERIFY_ERRORS_INPUT = 'VERIFY_ERRORS';
/**
- * The name of the [CONSTRUCTORS_ERRORS] input.
- */
- static const String CONSTRUCTORS_ERRORS_INPUT = 'CONSTRUCTORS_ERRORS';
-
- /**
* The task descriptor describing this kind of task.
*/
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
@@ -2637,7 +2302,6 @@
// Prepare inputs.
//
List<List<AnalysisError>> errorLists = <List<AnalysisError>>[];
- errorLists.addAll(getRequiredInput(CONSTRUCTORS_ERRORS_INPUT));
errorLists.add(getRequiredInput(HINTS_INPUT));
errorLists.add(getRequiredInput(RESOLVE_REFERENCES_ERRORS_INPUT));
errorLists.add(getRequiredInput(RESOLVE_TYPE_NAMES_ERRORS_INPUT));
@@ -2657,10 +2321,6 @@
static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
LibrarySpecificUnit unit = target;
return <String, TaskInput>{
- CONSTRUCTORS_ERRORS_INPUT: COMPILATION_UNIT_ELEMENT
- .of(unit)
- .mappedToList((CompilationUnitElement element) => element.types)
- .toListOf(CONSTRUCTORS_ERRORS),
HINTS_INPUT: HINTS.of(unit),
RESOLVE_REFERENCES_ERRORS_INPUT: RESOLVE_REFERENCES_ERRORS.of(unit),
RESOLVE_TYPE_NAMES_ERRORS_INPUT: RESOLVE_TYPE_NAMES_ERRORS.of(unit),
@@ -3010,7 +2670,7 @@
*/
class ResolveLibraryReferencesTask extends SourceBasedAnalysisTask {
/**
- * The name of the [LIBRARY_ELEMENT6] input.
+ * The name of the [LIBRARY_ELEMENT5] input.
*/
static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
@@ -3060,7 +2720,7 @@
static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
Source source = target;
return <String, TaskInput>{
- LIBRARY_INPUT: LIBRARY_ELEMENT6.of(source),
+ LIBRARY_INPUT: LIBRARY_ELEMENT5.of(source),
UNITS_INPUT: UNITS.of(source).toList((Source unit) =>
RESOLVED_UNIT5.of(new LibrarySpecificUnit(source, unit))),
'resolvedUnits': IMPORT_EXPORT_SOURCE_CLOSURE
@@ -3140,7 +2800,7 @@
*/
class ResolveUnitReferencesTask extends SourceBasedAnalysisTask {
/**
- * The name of the [LIBRARY_ELEMENT6] input.
+ * The name of the [LIBRARY_ELEMENT5] input.
*/
static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
@@ -3185,8 +2845,9 @@
//
InheritanceManager inheritanceManager =
new InheritanceManager(libraryElement);
- AstVisitor visitor = new ResolverVisitor.con2(libraryElement,
- unitElement.source, typeProvider, inheritanceManager, errorListener);
+ AstVisitor visitor = new ResolverVisitor(
+ libraryElement, unitElement.source, typeProvider, errorListener,
+ inheritanceManager: inheritanceManager);
unit.accept(visitor);
//
// Record outputs.
@@ -3206,8 +2867,8 @@
return <String, TaskInput>{
'fullyBuiltLibraryElements': IMPORT_EXPORT_SOURCE_CLOSURE
.of(unit.library)
- .toListOf(LIBRARY_ELEMENT6),
- LIBRARY_INPUT: LIBRARY_ELEMENT6.of(unit.library),
+ .toListOf(LIBRARY_ELEMENT5),
+ LIBRARY_INPUT: LIBRARY_ELEMENT5.of(unit.library),
UNIT_INPUT: RESOLVED_UNIT4.of(unit),
TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request)
};
@@ -3271,7 +2932,7 @@
//
// Resolve TypeName nodes.
//
- TypeResolverVisitor visitor = new TypeResolverVisitor.con2(
+ TypeResolverVisitor visitor = new TypeResolverVisitor(
library, unitElement.source, typeProvider, errorListener);
unit.accept(visitor);
//
@@ -3313,7 +2974,7 @@
*/
class ResolveVariableReferencesTask extends SourceBasedAnalysisTask {
/**
- * The name of the [LIBRARY_ELEMENT6] input.
+ * The name of the [LIBRARY_ELEMENT1] input.
*/
static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
@@ -3355,8 +3016,9 @@
//
TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
Scope nameScope = new LibraryScope(libraryElement, errorListener);
- AstVisitor visitor = new VariableResolverVisitor.con2(libraryElement,
- unitElement.source, typeProvider, nameScope, errorListener);
+ AstVisitor visitor = new VariableResolverVisitor(
+ libraryElement, unitElement.source, typeProvider, errorListener,
+ nameScope: nameScope);
unit.accept(visitor);
//
// Record outputs.
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index ab17aa4..961627c 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
name: analyzer
-version: 0.25.1-alpha.4
+version: 0.25.1
author: Dart Team <misc@dartlang.org>
description: Static analyzer for Dart.
homepage: http://www.dartlang.org
@@ -8,6 +8,7 @@
dependencies:
args: '>=0.12.1 <0.14.0'
html: ^0.12.0
+ package_config: ^0.1.1
path: '>=0.9.0 <2.0.0'
plugin: ^0.1.0
watcher: '>=0.9.6 <0.10.0'
diff --git a/pkg/analyzer/test/file_system/physical_resource_provider_test.dart b/pkg/analyzer/test/file_system/physical_resource_provider_test.dart
index cd9cfbb..7898bb9 100644
--- a/pkg/analyzer/test/file_system/physical_resource_provider_test.dart
+++ b/pkg/analyzer/test/file_system/physical_resource_provider_test.dart
@@ -296,7 +296,14 @@
file.deleteSync();
return _delayed(() {
expect(changesReceived, hasLength(1));
- expect(changesReceived[0].type, equals(ChangeType.REMOVE));
+ if (io.Platform.isWindows) {
+ // TODO(danrubel) https://github.com/dart-lang/sdk/issues/23762
+ // This test fails on Windows but a similar test in the underlying
+ // watcher package passes on Windows.
+ expect(changesReceived[0].type, equals(ChangeType.MODIFY));
+ } else {
+ expect(changesReceived[0].type, equals(ChangeType.REMOVE));
+ }
expect(changesReceived[0].path, equals(path));
});
});
diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart
index e163fd0..0a1b03a 100644
--- a/pkg/analyzer/test/generated/all_the_rest_test.dart
+++ b/pkg/analyzer/test/generated/all_the_rest_test.dart
@@ -1061,6 +1061,8 @@
ElementFactory.constructorElement(classElement, '', true);
constructorDeclaration.element = constructorElement;
classElement.constructors = <ConstructorElement>[constructorElement];
+ } else {
+ classElement.constructors = ConstructorElement.EMPTY_LIST;
}
return variableDeclaration;
}
@@ -8256,7 +8258,6 @@
}
}
-
@reflectiveTest
class StringScannerTest extends AbstractScannerTest {
@override
diff --git a/pkg/analyzer/test/generated/compile_time_error_code_test.dart b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
index cfc2f62..e212444 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code_test.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
@@ -1912,10 +1912,7 @@
class M {}
class C = bool with M;''');
computeLibrarySourceErrors(source);
- assertErrors(source, [
- CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS,
- CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS
- ]);
+ assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
@@ -1933,10 +1930,7 @@
class M {}
class C = int with M;''');
computeLibrarySourceErrors(source);
- assertErrors(source, [
- CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS,
- CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS
- ]);
+ assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
@@ -1963,10 +1957,7 @@
class M {}
class C = String with M;''');
computeLibrarySourceErrors(source);
- assertErrors(source, [
- CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS,
- CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS
- ]);
+ assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
verify([source]);
}
@@ -5282,6 +5273,22 @@
verify([source]);
}
+ void test_recursiveInterfaceInheritance_mixin_superclass() {
+ // Make sure we don't get CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS in
+ // addition--that would just be confusing.
+ Source source = addSource('''
+class C = D with M;
+class D = C with M;
+class M {}
+''');
+ computeLibrarySourceErrors(source);
+ assertErrors(source, [
+ CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE,
+ CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE
+ ]);
+ verify([source]);
+ }
+
void test_recursiveInterfaceInheritance_tail() {
Source source = addSource(r'''
abstract class A implements A {}
diff --git a/pkg/analyzer/test/generated/element_test.dart b/pkg/analyzer/test/generated/element_test.dart
index 5bdceca..d5f9ffb 100644
--- a/pkg/analyzer/test/generated/element_test.dart
+++ b/pkg/analyzer/test/generated/element_test.dart
@@ -2380,6 +2380,7 @@
void test_getConstructors_empty() {
ClassElementImpl typeElement = ElementFactory.classElement2("A");
+ typeElement.constructors = ConstructorElement.EMPTY_LIST;
InterfaceTypeImpl type = new InterfaceTypeImpl(typeElement);
expect(type.constructors, isEmpty);
}
diff --git a/pkg/analyzer/test/generated/engine_test.dart b/pkg/analyzer/test/generated/engine_test.dart
index eb87ea7..8d9365c 100644
--- a/pkg/analyzer/test/generated/engine_test.dart
+++ b/pkg/analyzer/test/generated/engine_test.dart
@@ -10,6 +10,7 @@
import 'dart:async';
import 'dart:collection';
+import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/cancelable_future.dart';
import 'package:analyzer/src/context/cache.dart' show CacheEntry;
import 'package:analyzer/src/generated/ast.dart';
@@ -447,6 +448,42 @@
});
}
+ /**
+ * IDEA uses the following scenario:
+ * 1. Add overlay.
+ * 2. Change overlay.
+ * 3. If the contents of the document buffer is the same as the contents
+ * of the file, remove overlay.
+ * So, we need to try to use incremental resolution for removing overlays too.
+ */
+ void test_applyChanges_remove_incremental() {
+ MemoryResourceProvider resourceProvider = new MemoryResourceProvider();
+ Source source = resourceProvider.newFile('/test.dart', r'''
+main() {
+ print(1);
+}
+''').createSource();
+ _context = AnalysisContextFactory.oldContextWithCore();
+ _context.analysisOptions = new AnalysisOptionsImpl()..incremental = true;
+ _context.applyChanges(new ChangeSet()..addedSource(source));
+ // remember compilation unit
+ _analyzeAll_assertFinished();
+ CompilationUnit unit = _context.getResolvedCompilationUnit2(source, source);
+ // add overlay
+ _context.setContents(source, r'''
+main() {
+ print(12);
+}
+''');
+ _analyzeAll_assertFinished();
+ expect(_context.getResolvedCompilationUnit2(source, source), unit);
+ // remove overlay
+ _context.setContents(source, null);
+ _context.validateCacheConsistency();
+ _analyzeAll_assertFinished();
+ expect(_context.getResolvedCompilationUnit2(source, source), unit);
+ }
+
Future test_applyChanges_removeContainer() {
_context = AnalysisContextFactory.oldContextWithCore();
SourcesChangedListener listener = new SourcesChangedListener();
diff --git a/pkg/analyzer/test/generated/incremental_resolver_test.dart b/pkg/analyzer/test/generated/incremental_resolver_test.dart
index 98f6d23..ed41b59 100644
--- a/pkg/analyzer/test/generated/incremental_resolver_test.dart
+++ b/pkg/analyzer/test/generated/incremental_resolver_test.dart
@@ -2486,7 +2486,7 @@
void resetWithOptions(AnalysisOptions options) {
if (AnalysisEngine.instance.useTaskModel) {
analysisContext2 =
- AnalysisContextFactory.contextWithCoreAndOptions(options);
+ AnalysisContextFactory.contextWithCoreAndOptions(options);
} else {
analysisContext2 =
AnalysisContextFactory.oldContextWithCoreAndOptions(options);
@@ -3711,7 +3711,7 @@
expect(errors, isEmpty);
}
- void test_updateErrors_addNew_hint() {
+ void test_updateErrors_addNew_hint1() {
_resolveUnit(r'''
int main() {
return 42;
@@ -3723,7 +3723,7 @@
''');
}
- void test_updateErrors_addNew_hints() {
+ void test_updateErrors_addNew_hint2() {
_resolveUnit(r'''
main() {
int v = 0;
diff --git a/pkg/analyzer/test/generated/non_error_resolver_test.dart b/pkg/analyzer/test/generated/non_error_resolver_test.dart
index e36ab22..06336a4 100644
--- a/pkg/analyzer/test/generated/non_error_resolver_test.dart
+++ b/pkg/analyzer/test/generated/non_error_resolver_test.dart
@@ -3393,6 +3393,18 @@
verify([source]);
}
+ void test_nonAbstractClassInheritsAbstractMemberOne_overridesMethodInObject() {
+ Source source = addSource(r'''
+class A {
+ String toString([String prefix = '']) => '${prefix}Hello';
+}
+class C {}
+class B extends A with C {}''');
+ computeLibrarySourceErrors(source);
+ assertNoErrors(source);
+ verify([source]);
+ }
+
void test_nonBoolExpression_functionType() {
Source source = addSource(r'''
bool makeAssertion() => true;
diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart
index a796d5e..e07053d 100644
--- a/pkg/analyzer/test/generated/resolver_test.dart
+++ b/pkg/analyzer/test/generated/resolver_test.dart
@@ -1934,7 +1934,10 @@
_definingLibrary.definingCompilationUnit = definingCompilationUnit;
Library library = new Library(context, _listener, source);
library.libraryElement = _definingLibrary;
- _visitor = new ResolverVisitor.con1(library, source, _typeProvider);
+ _visitor = new ResolverVisitor(
+ library.libraryElement, source, _typeProvider, library.errorListener,
+ nameScope: library.libraryScope,
+ inheritanceManager: library.inheritanceManager);
try {
return _visitor.elementResolver;
} catch (exception) {
@@ -11084,7 +11087,10 @@
definingLibrary.definingCompilationUnit = definingCompilationUnit;
Library library = new Library(context, _listener, source);
library.libraryElement = definingLibrary;
- _visitor = new ResolverVisitor.con1(library, source, _typeProvider);
+ _visitor = new ResolverVisitor(
+ library.libraryElement, source, _typeProvider, library.errorListener,
+ nameScope: library.libraryScope,
+ inheritanceManager: library.inheritanceManager);
_visitor.overrideManager.enterScope();
try {
return _visitor.typeAnalyzer;
@@ -13365,11 +13371,6 @@
*/
TypeResolverVisitor _visitor;
- /**
- * The visitor used to resolve types needed to form the type hierarchy.
- */
- ImplicitConstructorBuilder _implicitConstructorBuilder;
-
void fail_visitConstructorDeclaration() {
fail("Not yet tested");
_listener.assertNoErrors();
@@ -13418,16 +13419,9 @@
new CompilationUnitElementImpl("lib.dart");
_library.libraryElement = element;
_typeProvider = new TestTypeProvider();
- _visitor =
- new TypeResolverVisitor.con1(_library, librarySource, _typeProvider);
- _implicitConstructorBuilder = new ImplicitConstructorBuilder(_listener,
- (ClassElement classElement, ClassElement superclassElement,
- void computation()) {
- // For these tests, we assume the classes for which implicit
- // constructors need to be built are visited in proper dependency order,
- // so we just invoke the computation immediately.
- computation();
- });
+ _visitor = new TypeResolverVisitor(_library.libraryElement, librarySource,
+ _typeProvider, _library.errorListener,
+ nameScope: _library.libraryScope);
}
void test_visitCatchClause_exception() {
@@ -13827,9 +13821,6 @@
}
}
node.accept(_visitor);
- if (node is Declaration) {
- node.element.accept(_implicitConstructorBuilder);
- }
}
}
diff --git a/pkg/analyzer/test/generated/source_factory_test.dart b/pkg/analyzer/test/generated/source_factory_test.dart
index 26b3942..e510949 100644
--- a/pkg/analyzer/test/generated/source_factory_test.dart
+++ b/pkg/analyzer/test/generated/source_factory_test.dart
@@ -7,6 +7,8 @@
library analyzer.test.generated.source_factory;
+import 'dart:convert';
+
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/source/package_map_resolver.dart';
@@ -15,6 +17,10 @@
import 'package:analyzer/src/generated/java_io.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart' as utils;
+import 'package:package_config/packages.dart';
+import 'package:package_config/packages_file.dart' as pkgfile show parse;
+import 'package:package_config/src/packages_impl.dart';
import 'package:path/path.dart';
import 'package:unittest/unittest.dart';
@@ -24,6 +30,147 @@
main() {
groupSep = ' | ';
runReflectiveTests(SourceFactoryTest);
+ runPackageMapTests();
+}
+
+void runPackageMapTests() {
+ final Uri baseUri = new Uri.file('test/base');
+ final List<UriResolver> testResolvers = [new FileUriResolver()];
+
+ Packages createPackageMap(Uri base, String configFileContents) {
+ List<int> bytes = UTF8.encode(configFileContents);
+ Map<String, Uri> map = pkgfile.parse(bytes, base);
+ return new MapPackages(map);
+ }
+
+ Map<String, List<Folder>> getPackageMap(String config) {
+ Packages packages = createPackageMap(baseUri, config);
+ SourceFactory factory = new SourceFactory(testResolvers, packages);
+ return factory.packageMap;
+ }
+
+ String resolvePackageUri({String uri, String config, Source containingSource,
+ UriResolver customResolver}) {
+ Packages packages = createPackageMap(baseUri, config);
+ List<UriResolver> resolvers = testResolvers.toList();
+ if (customResolver != null) {
+ resolvers.add(customResolver);
+ }
+ SourceFactory factory = new SourceFactory(resolvers, packages);
+ Source source = factory.resolveUri(containingSource, uri);
+ return source != null ? source.fullName : null;
+ }
+
+ Uri restorePackageUri(
+ {Source source, String config, UriResolver customResolver}) {
+ Packages packages = createPackageMap(baseUri, config);
+ List<UriResolver> resolvers = testResolvers.toList();
+ if (customResolver != null) {
+ resolvers.add(customResolver);
+ }
+ SourceFactory factory = new SourceFactory(resolvers, packages);
+ return factory.restoreUri(source);
+ }
+
+ group('SourceFactoryTest', () {
+ group('package mapping', () {
+ group('resolveUri', () {
+ test('URI in mapping', () {
+ String uri = resolvePackageUri(config: '''
+unittest:/home/somebody/.pub/cache/unittest-0.9.9/lib/
+async:/home/somebody/.pub/cache/async-1.1.0/lib/
+quiver:/home/somebody/.pub/cache/quiver-1.2.1/lib
+''', uri: 'package:unittest/unittest.dart');
+ expect(uri, equals(
+ '/home/somebody/.pub/cache/unittest-0.9.9/lib/unittest.dart'));
+ });
+ test('URI not in mapping', () {
+ String uri = resolvePackageUri(
+ config: 'unittest:/home/somebody/.pub/cache/unittest-0.9.9/lib/',
+ uri: 'package:foo/foo.dart');
+ expect(uri, isNull);
+ });
+ test('Non-package URI', () {
+ var testResolver = new CustomUriResolver(uriPath: 'test_uri');
+ String uri = resolvePackageUri(
+ config: 'unittest:/home/somebody/.pub/cache/unittest-0.9.9/lib/',
+ uri: 'custom:custom.dart',
+ customResolver: testResolver);
+ expect(uri, testResolver.uriPath);
+ });
+ test('Invalid URI', () {
+ // TODO(pquitslund): fix clients to handle errors appropriately
+ // CLI: print message 'invalid package file format'
+ // SERVER: best case tell user somehow and recover...
+ expect(() => resolvePackageUri(
+ config: 'foo:<:&%>', uri: 'package:foo/bar.dart'),
+ throwsA(new isInstanceOf('FormatException')));
+ });
+ test('Valid URI that cannot be further resolved', () {
+ String uri = resolvePackageUri(
+ config: 'foo:http://www.google.com', uri: 'package:foo/bar.dart');
+ expect(uri, isNull);
+ });
+ test('Relative URIs', () {
+ Source containingSource = createSource(
+ path: '/foo/bar/baz/foo.dart', uri: 'package:foo/foo.dart');
+ String uri = resolvePackageUri(
+ config: 'foo:/foo/bar/baz',
+ uri: 'bar.dart',
+ containingSource: containingSource);
+ expect(uri, isNotNull);
+ expect(uri, equals('/foo/bar/baz/bar.dart'));
+ });
+ });
+ group('restoreUri', () {
+ test('URI in mapping', () {
+ Uri uri = restorePackageUri(config: '''
+unittest:/home/somebody/.pub/cache/unittest-0.9.9/lib/
+async:/home/somebody/.pub/cache/async-1.1.0/lib/
+quiver:/home/somebody/.pub/cache/quiver-1.2.1/lib
+''',
+ source: new FileBasedSource(FileUtilities2.createFile(
+ '/home/somebody/.pub/cache/unittest-0.9.9/lib/unittest.dart')));
+ expect(uri, isNotNull);
+ expect(uri.toString(), equals('package:unittest/unittest.dart'));
+ });
+ });
+ group('packageMap', () {
+ test('non-file URIs filtered', () {
+ Map<String, List<Folder>> map = getPackageMap('''
+quiver:/home/somebody/.pub/cache/quiver-1.2.1/lib
+foo:http://www.google.com
+''');
+ expect(map.keys, unorderedEquals(['quiver']));
+ });
+ });
+ });
+ });
+
+ group('URI utils', () {
+ group('URI', () {
+ test('startsWith', () {
+ expect(utils.startsWith(Uri.parse('/foo/bar/'), Uri.parse('/foo/')),
+ isTrue);
+ expect(utils.startsWith(Uri.parse('/foo/bar/'), Uri.parse('/foo/bar/')),
+ isTrue);
+ expect(utils.startsWith(Uri.parse('/foo/bar'), Uri.parse('/foo/b')),
+ isFalse);
+ });
+ });
+ });
+}
+
+Source createSource({String path, String uri}) => new MemoryResourceProvider()
+ .getFile(path)
+ .createSource(uri != null ? Uri.parse(uri) : null);
+
+class CustomUriResolver extends UriResolver {
+ String uriPath;
+ CustomUriResolver({this.uriPath});
+
+ @override
+ Source resolveAbsolute(Uri uri) => createSource(path: uriPath);
}
@reflectiveTest
diff --git a/pkg/analyzer/test/src/context/cache_test.dart b/pkg/analyzer/test/src/context/cache_test.dart
index 2a3952a..c6d9254 100644
--- a/pkg/analyzer/test/src/context/cache_test.dart
+++ b/pkg/analyzer/test/src/context/cache_test.dart
@@ -237,6 +237,27 @@
@reflectiveTest
class CacheEntryTest extends AbstractCacheTest {
+ test_dispose() {
+ ResultDescriptor descriptor1 = new ResultDescriptor('result1', -1);
+ ResultDescriptor descriptor2 = new ResultDescriptor('result2', -2);
+ AnalysisTarget target1 = new TestSource('1.dart');
+ AnalysisTarget target2 = new TestSource('2.dart');
+ TargetedResult result1 = new TargetedResult(target1, descriptor1);
+ TargetedResult result2 = new TargetedResult(target2, descriptor2);
+ CacheEntry entry1 = new CacheEntry(target1);
+ CacheEntry entry2 = new CacheEntry(target2);
+ cache.put(entry1);
+ cache.put(entry2);
+ entry1.setValue(descriptor1, 1, TargetedResult.EMPTY_LIST);
+ entry2.setValue(descriptor2, 2, <TargetedResult>[result1]);
+ // target2 is listed as dependent in target1
+ expect(
+ entry1.getResultData(descriptor1).dependentResults, contains(result2));
+ // dispose entry2, result2 is removed from result1
+ entry2.dispose();
+ expect(entry1.getResultData(descriptor1).dependentResults, isEmpty);
+ }
+
test_explicitlyAdded() {
AnalysisTarget target = new TestSource();
CacheEntry entry = new CacheEntry(target);
@@ -983,6 +1004,37 @@
return new UniversalCachePartition(null);
}
+ test_dispose() {
+ InternalAnalysisContext context = new _InternalAnalysisContextMock();
+ CachePartition partition1 = new UniversalCachePartition(context);
+ CachePartition partition2 = new UniversalCachePartition(context);
+ AnalysisCache cache = new AnalysisCache([partition1, partition2]);
+ when(context.analysisCache).thenReturn(cache);
+ // configure
+ // prepare entries
+ ResultDescriptor descriptor1 = new ResultDescriptor('result1', -1);
+ ResultDescriptor descriptor2 = new ResultDescriptor('result2', -2);
+ AnalysisTarget target1 = new TestSource('1.dart');
+ AnalysisTarget target2 = new TestSource('2.dart');
+ TargetedResult result1 = new TargetedResult(target1, descriptor1);
+ TargetedResult result2 = new TargetedResult(target2, descriptor2);
+ CacheEntry entry1 = new CacheEntry(target1);
+ CacheEntry entry2 = new CacheEntry(target2);
+ partition1.put(entry1);
+ partition2.put(entry2);
+ entry1.setValue(descriptor1, 1, TargetedResult.EMPTY_LIST);
+ entry2.setValue(descriptor2, 2, <TargetedResult>[result1]);
+ // target2 is listed as dependent in target1
+ expect(
+ entry1.getResultData(descriptor1).dependentResults, contains(result2));
+ // dispose
+ partition2.dispose();
+ expect(partition1.get(target1), same(entry1));
+ expect(partition2.get(target2), isNull);
+ // result2 is removed from result1
+ expect(entry1.getResultData(descriptor1).dependentResults, isEmpty);
+ }
+
void test_contains() {
UniversalCachePartition partition = new UniversalCachePartition(null);
TestSource source = new TestSource();
diff --git a/pkg/analyzer/test/src/context/context_test.dart b/pkg/analyzer/test/src/context/context_test.dart
index af84f24..7bc47ba 100644
--- a/pkg/analyzer/test/src/context/context_test.dart
+++ b/pkg/analyzer/test/src/context/context_test.dart
@@ -6,6 +6,7 @@
import 'dart:async';
+import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/cancelable_future.dart';
import 'package:analyzer/src/context/cache.dart';
import 'package:analyzer/src/context/context.dart';
@@ -277,6 +278,41 @@
});
}
+ /**
+ * IDEA uses the following scenario:
+ * 1. Add overlay.
+ * 2. Change overlay.
+ * 3. If the contents of the document buffer is the same as the contents
+ * of the file, remove overlay.
+ * So, we need to try to use incremental resolution for removing overlays too.
+ */
+ void test_applyChanges_remove_incremental() {
+ MemoryResourceProvider resourceProvider = new MemoryResourceProvider();
+ Source source = resourceProvider.newFile('/test.dart', r'''
+main() {
+ print(1);
+}
+''').createSource();
+ context.analysisOptions = new AnalysisOptionsImpl()..incremental = true;
+ context.applyChanges(new ChangeSet()..addedSource(source));
+ // remember compilation unit
+ _analyzeAll_assertFinished();
+ CompilationUnit unit = context.getResolvedCompilationUnit2(source, source);
+ // add overlay
+ context.setContents(source, r'''
+main() {
+ print(12);
+}
+''');
+ _analyzeAll_assertFinished();
+ expect(context.getResolvedCompilationUnit2(source, source), unit);
+ // remove overlay
+ context.setContents(source, null);
+ context.validateCacheConsistency();
+ _analyzeAll_assertFinished();
+ expect(context.getResolvedCompilationUnit2(source, source), unit);
+ }
+
Future test_applyChanges_removeContainer() {
SourcesChangedListener listener = new SourcesChangedListener();
context.onSourcesChanged.listen(listener.onData);
diff --git a/pkg/analyzer/test/src/task/dart_test.dart b/pkg/analyzer/test/src/task/dart_test.dart
index c89e6e9..77db010 100644
--- a/pkg/analyzer/test/src/task/dart_test.dart
+++ b/pkg/analyzer/test/src/task/dart_test.dart
@@ -28,14 +28,12 @@
main() {
groupSep = ' | ';
- runReflectiveTests(BuildClassConstructorsTaskTest);
runReflectiveTests(BuildCompilationUnitElementTaskTest);
runReflectiveTests(BuildDirectiveElementsTaskTest);
runReflectiveTests(BuildEnumMemberElementsTaskTest);
runReflectiveTests(BuildSourceExportClosureTaskTest);
runReflectiveTests(BuildSourceImportExportClosureTaskTest);
runReflectiveTests(BuildExportNamespaceTaskTest);
- runReflectiveTests(BuildLibraryConstructorsTaskTest);
runReflectiveTests(BuildLibraryElementTaskTest);
runReflectiveTests(BuildPublicNamespaceTaskTest);
runReflectiveTests(BuildTypeProviderTaskTest);
@@ -59,112 +57,6 @@
}
@reflectiveTest
-class BuildClassConstructorsTaskTest extends _AbstractDartTaskTest {
- test_perform_ClassDeclaration_errors_mixinHasNoConstructors() {
- Source source = newSource('/test.dart', '''
-class B {
- B({x});
-}
-class M {}
-class C extends B with M {}
-''');
- LibraryElement libraryElement;
- {
- computeResult(source, LIBRARY_ELEMENT5);
- libraryElement = outputs[LIBRARY_ELEMENT5];
- }
- // prepare C
- ClassElement c = libraryElement.getType('C');
- expect(c, isNotNull);
- // build constructors
- computeResult(c, CONSTRUCTORS);
- expect(task, new isInstanceOf<BuildClassConstructorsTask>());
- _fillErrorListener(CONSTRUCTORS_ERRORS);
- errorListener.assertErrorsWithCodes(
- <ErrorCode>[CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS]);
- }
-
- test_perform_ClassDeclaration_explicitConstructors() {
- Source source = newSource('/test.dart', '''
-class B {
- B(p);
-}
-class C extends B {
- C(int a, String b) {}
-}
-''');
- LibraryElement libraryElement;
- {
- computeResult(source, LIBRARY_ELEMENT5);
- libraryElement = outputs[LIBRARY_ELEMENT5];
- }
- // prepare C
- ClassElement c = libraryElement.getType('C');
- expect(c, isNotNull);
- // build constructors
- computeResult(c, CONSTRUCTORS);
- expect(task, new isInstanceOf<BuildClassConstructorsTask>());
- // no errors
- expect(outputs[CONSTRUCTORS_ERRORS], isEmpty);
- // explicit constructor
- List<ConstructorElement> constructors = outputs[CONSTRUCTORS];
- expect(constructors, hasLength(1));
- expect(constructors[0].parameters, hasLength(2));
- }
-
- test_perform_ClassTypeAlias() {
- Source source = newSource('/test.dart', '''
-class B {
- B(int i);
-}
-class M1 {}
-class M2 {}
-
-class C2 = C1 with M2;
-class C1 = B with M1;
-''');
- LibraryElement libraryElement;
- {
- computeResult(source, LIBRARY_ELEMENT5);
- libraryElement = outputs[LIBRARY_ELEMENT5];
- }
- // prepare C2
- ClassElement class2 = libraryElement.getType('C2');
- expect(class2, isNotNull);
- // build constructors
- computeResult(class2, CONSTRUCTORS);
- expect(task, new isInstanceOf<BuildClassConstructorsTask>());
- List<ConstructorElement> constructors = outputs[CONSTRUCTORS];
- expect(constructors, hasLength(1));
- expect(constructors[0].parameters, hasLength(1));
- }
-
- test_perform_ClassTypeAlias_errors_mixinHasNoConstructors() {
- Source source = newSource('/test.dart', '''
-class B {
- B({x});
-}
-class M {}
-class C = B with M;
-''');
- LibraryElement libraryElement;
- {
- computeResult(source, LIBRARY_ELEMENT5);
- libraryElement = outputs[LIBRARY_ELEMENT5];
- }
- // prepare C
- ClassElement c = libraryElement.getType('C');
- expect(c, isNotNull);
- // build constructors
- computeResult(c, CONSTRUCTORS);
- expect(task, new isInstanceOf<BuildClassConstructorsTask>());
- _fillErrorListener(CONSTRUCTORS_ERRORS);
- errorListener.assertErrorsWithCodes(
- <ErrorCode>[CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS]);
- }
-}
-
-@reflectiveTest
class BuildCompilationUnitElementTaskTest extends _AbstractDartTaskTest {
Source source;
LibrarySpecificUnit target;
@@ -715,40 +607,6 @@
}
@reflectiveTest
-class BuildLibraryConstructorsTaskTest extends _AbstractDartTaskTest {
- test_perform() {
- Source source = newSource('/test.dart', '''
-class B {
- B(int i);
-}
-class M1 {}
-class M2 {}
-
-class C2 = C1 with M2;
-class C1 = B with M1;
-class C3 = B with M2;
-''');
- computeResult(source, LIBRARY_ELEMENT6);
- expect(task, new isInstanceOf<BuildLibraryConstructorsTask>());
- LibraryElement libraryElement = outputs[LIBRARY_ELEMENT6];
- // C1
- {
- ClassElement classElement = libraryElement.getType('C2');
- List<ConstructorElement> constructors = classElement.constructors;
- expect(constructors, hasLength(1));
- expect(constructors[0].parameters, hasLength(1));
- }
- // C3
- {
- ClassElement classElement = libraryElement.getType('C3');
- List<ConstructorElement> constructors = classElement.constructors;
- expect(constructors, hasLength(1));
- expect(constructors[0].parameters, hasLength(1));
- }
- }
-}
-
-@reflectiveTest
class BuildLibraryElementTaskTest extends _AbstractDartTaskTest {
Source librarySource;
CompilationUnit libraryUnit;
@@ -798,7 +656,7 @@
part of lib;
'''
});
- expect(outputs, hasLength(4));
+ expect(outputs, hasLength(3));
// simple outputs
expect(outputs[BUILD_LIBRARY_ERRORS], isEmpty);
expect(outputs[IS_LAUNCHABLE], isFalse);
@@ -839,28 +697,6 @@
(libraryUnit.directives[2] as PartDirective).element, same(secondPart));
}
- test_perform_classElements() {
- _performBuildTask({
- '/lib.dart': '''
-library lib;
-part 'part1.dart';
-part 'part2.dart';
-class A {}
-''',
- '/part1.dart': '''
-part of lib;
-class B {}
-''',
- '/part2.dart': '''
-part of lib;
-class C {}
-'''
- });
- List<ClassElement> classElements = outputs[CLASS_ELEMENTS];
- List<String> classNames = classElements.map((c) => c.displayName).toList();
- expect(classNames, unorderedEquals(['A', 'B', 'C']));
- }
-
test_perform_error_missingLibraryDirectiveWithPart_hasCommon() {
_performBuildTask({
'/lib.dart': '''
@@ -2054,7 +1890,6 @@
.buildInputs(new LibrarySpecificUnit(emptySource, emptySource));
expect(inputs, isNotNull);
expect(inputs.keys, unorderedEquals([
- LibraryUnitErrorsTask.CONSTRUCTORS_ERRORS_INPUT,
LibraryUnitErrorsTask.HINTS_INPUT,
LibraryUnitErrorsTask.RESOLVE_REFERENCES_ERRORS_INPUT,
LibraryUnitErrorsTask.RESOLVE_TYPE_NAMES_ERRORS_INPUT,
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
index a940dbf..9fd865a 100644
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ b/pkg/compiler/lib/src/apiimpl.dart
@@ -60,6 +60,8 @@
trustPrimitives:
hasOption(options, '--trust-primitives'),
enableMinification: hasOption(options, '--minify'),
+ useFrequencyNamer:
+ !hasOption(options, "--no-frequency-based-minification"),
preserveUris: hasOption(options, '--preserve-uris'),
enableNativeLiveTypeAnalysis:
!hasOption(options, '--disable-native-live-type-analysis'),
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 88368dd..8360a5d 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -26,8 +26,6 @@
*/
final AstElement element;
- TreeElements get resolutionTree;
-
WorkItem(this.element, this.compilationContext) {
assert(invariant(element, element.isDeclaration));
}
@@ -37,7 +35,7 @@
/// [WorkItem] used exclusively by the [ResolutionEnqueuer].
class ResolutionWorkItem extends WorkItem {
- TreeElements resolutionTree;
+ bool _isAnalyzed = false;
ResolutionWorkItem(AstElement element,
ItemCompilationContext compilationContext)
@@ -45,11 +43,11 @@
WorldImpact run(Compiler compiler, ResolutionEnqueuer world) {
WorldImpact impact = compiler.analyze(this, world);
- resolutionTree = element.resolvedAst.elements;
+ _isAnalyzed = true;
return impact;
}
- bool isAnalyzed() => resolutionTree != null;
+ bool get isAnalyzed => _isAnalyzed;
}
// TODO(johnniwinther): Split this class into interface and implementation.
@@ -272,6 +270,11 @@
/// Backend callback methods for the resolution phase.
ResolutionCallbacks get resolutionCallbacks;
+ /// The strategy used for collecting and emitting source information.
+ SourceInformationStrategy get sourceInformationStrategy {
+ return const SourceInformationStrategy();
+ }
+
// TODO(johnniwinther): Move this to the JavaScriptBackend.
String get patchVersion => null;
@@ -699,6 +702,8 @@
final bool enableMinification;
+ final bool useFrequencyNamer;
+
/// When `true` emits URIs in the reflection metadata.
final bool preserveUris;
@@ -944,6 +949,7 @@
ParserTask parser;
PatchParserTask patchParser;
LibraryLoaderTask libraryLoader;
+ SerializationTask serialization;
ResolverTask resolver;
closureMapping.ClosureTask closureToClassMapper;
TypeCheckerTask checker;
@@ -1042,6 +1048,7 @@
bool analyzeSignaturesOnly: false,
this.preserveComments: false,
this.useCpsIr: false,
+ this.useFrequencyNamer: false,
this.verbose: false,
this.sourceMapUri: null,
this.outputUri: null,
@@ -1093,17 +1100,10 @@
globalDependencies =
new CodegenRegistry(this, new TreeElementMapping(null));
- SourceInformationFactory sourceInformationFactory =
- const SourceInformationFactory();
- if (generateSourceMap) {
- sourceInformationFactory =
- const bool.fromEnvironment('USE_NEW_SOURCE_INFO', defaultValue: false)
- ? const PositionSourceInformationFactory()
- : const StartEndSourceInformationFactory();
- }
if (emitJavaScript) {
- js_backend.JavaScriptBackend jsBackend = new js_backend.JavaScriptBackend(
- this, sourceInformationFactory, generateSourceMap: generateSourceMap);
+ js_backend.JavaScriptBackend jsBackend =
+ new js_backend.JavaScriptBackend(
+ this, generateSourceMap: generateSourceMap);
backend = jsBackend;
} else {
backend = new dart_backend.DartBackend(this, strips,
@@ -1115,6 +1115,7 @@
tasks = [
libraryLoader = new LibraryLoaderTask(this),
+ serialization = new SerializationTask(this),
scanner = new ScannerTask(this),
dietParser = new DietParserTask(this),
parser = new ParserTask(this),
@@ -1122,7 +1123,7 @@
resolver = new ResolverTask(this, backend.constantCompilerTask),
closureToClassMapper = new closureMapping.ClosureTask(this),
checker = new TypeCheckerTask(this),
- irBuilder = new IrBuilderTask(this, sourceInformationFactory),
+ irBuilder = new IrBuilderTask(this, backend.sourceInformationStrategy),
typesTask = new ti.TypesTask(this),
constants = backend.constantCompilerTask,
deferredLoadTask = new DeferredLoadTask(this),
@@ -1815,7 +1816,7 @@
WorldImpact analyze(ResolutionWorkItem work, ResolutionEnqueuer world) {
assert(invariant(work.element, identical(world, enqueuer.resolution)));
- assert(invariant(work.element, !work.isAnalyzed(),
+ assert(invariant(work.element, !work.isAnalyzed,
message: 'Element ${work.element} has already been analyzed'));
if (shouldPrintProgress) {
// TODO(ahe): Add structured diagnostics to the compiler API and
@@ -2239,6 +2240,20 @@
return f(beginOffset, endOffset);
}
+ int get hashCode {
+ return 13 * uri.hashCode +
+ 17 * begin.hashCode +
+ 19 * end.hashCode;
+ }
+
+ bool operator ==(other) {
+ if (identical(this, other)) return true;
+ if (other is! SourceSpan) return false;
+ return uri == other.uri &&
+ begin == other.begin &&
+ end == other.end;
+ }
+
String toString() => 'SourceSpan($uri, $begin, $end)';
}
@@ -2413,22 +2428,22 @@
class _CompilerCoreTypes implements CoreTypes {
final Compiler compiler;
- ClassElementX objectClass;
- ClassElementX boolClass;
- ClassElementX numClass;
- ClassElementX intClass;
- ClassElementX doubleClass;
- ClassElementX stringClass;
- ClassElementX functionClass;
- ClassElementX nullClass;
- ClassElementX listClass;
- ClassElementX typeClass;
- ClassElementX mapClass;
- ClassElementX symbolClass;
- ClassElementX stackTraceClass;
- ClassElementX futureClass;
- ClassElementX iterableClass;
- ClassElementX streamClass;
+ ClassElement objectClass;
+ ClassElement boolClass;
+ ClassElement numClass;
+ ClassElement intClass;
+ ClassElement doubleClass;
+ ClassElement stringClass;
+ ClassElement functionClass;
+ ClassElement nullClass;
+ ClassElement listClass;
+ ClassElement typeClass;
+ ClassElement mapClass;
+ ClassElement symbolClass;
+ ClassElement stackTraceClass;
+ ClassElement futureClass;
+ ClassElement iterableClass;
+ ClassElement streamClass;
_CompilerCoreTypes(this.compiler);
diff --git a/pkg/compiler/lib/src/constants/constructors.dart b/pkg/compiler/lib/src/constants/constructors.dart
index a92edcc..1e588a8 100644
--- a/pkg/compiler/lib/src/constants/constructors.dart
+++ b/pkg/compiler/lib/src/constants/constructors.dart
@@ -135,7 +135,7 @@
NodeList parameters,
Node body, _) {
// TODO(johnniwinther): Handle constant constructors with errors.
- internalError(node, "Factory constructor cannot be constant.");
+ internalError(node, "Factory constructor cannot be constant: $node.");
}
applyParameters(NodeList parameters, _) {
diff --git a/pkg/compiler/lib/src/constants/expressions.dart b/pkg/compiler/lib/src/constants/expressions.dart
index dc0a3d9..4b1060c 100644
--- a/pkg/compiler/lib/src/constants/expressions.dart
+++ b/pkg/compiler/lib/src/constants/expressions.dart
@@ -108,6 +108,22 @@
Map<FieldElement, ConstantExpression> computeInstanceFields(
List<ConstantExpression> arguments,
CallStructure callStructure);
+
+ accept(ConstantConstructorVisitor visitor, arg);
+}
+
+abstract class ConstantConstructorVisitor<R, A> {
+ const ConstantConstructorVisitor();
+
+ R visit(ConstantConstructor constantConstructor, A context) {
+ return constantConstructor.accept(this, context);
+ }
+
+ R visitGenerative(GenerativeConstantConstructor constructor, A arg);
+ R visitRedirectingGenerative(
+ RedirectingGenerativeConstantConstructor constructor, A arg);
+ R visitRedirectingFactory(
+ RedirectingFactoryConstantConstructor constructor, A arg);
}
/// A generative constant constructor.
@@ -142,6 +158,10 @@
return appliedFieldMap;
}
+ accept(ConstantConstructorVisitor visitor, arg) {
+ return visitor.visitGenerative(this, arg);
+ }
+
int get hashCode {
int hash = Hashing.objectHash(type);
hash = Hashing.mapHash(defaultValues, hash);
@@ -233,6 +253,10 @@
return appliedFieldMap;
}
+ accept(ConstantConstructorVisitor visitor, arg) {
+ return visitor.visitRedirectingGenerative(this, arg);
+ }
+
int get hashCode {
int hash = Hashing.objectHash(thisConstructorInvocation);
return Hashing.mapHash(defaultValues, hash);
@@ -282,6 +306,10 @@
return constantConstructor.computeInstanceFields(arguments, callStructure);
}
+ accept(ConstantConstructorVisitor visitor, arg) {
+ return visitor.visitRedirectingFactory(this, arg);
+ }
+
int get hashCode {
return Hashing.objectHash(targetConstructorInvocation);
}
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
index df98350..6c324d8 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
@@ -191,6 +191,12 @@
}
}
}
+
+ /// True if a jump inserted now will escape from a try block.
+ ///
+ /// Concretely, this is true when [enterTry] has been called without
+ /// its corresponding [leaveTry] call.
+ bool get isEscapingTry => _boxedTryVariables.isNotEmpty;
}
/// A class to collect 'forward' jumps.
@@ -241,7 +247,8 @@
void addJump(IrBuilder builder, [ir.Primitive value]) {
assert(_continuation == null);
_buildTryExit(builder);
- ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized();
+ ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized(
+ isEscapingTry: isEscapingTry);
builder.add(invoke);
_invocations.add(invoke);
// Truncate the environment at the invocation site so it only includes
@@ -360,7 +367,8 @@
if (value != null) builder.environment.extend(null, value);
builder.add(new ir.InvokeContinuation(_continuation,
builder.environment.index2value,
- isRecursive: true));
+ isRecursive: true,
+ isEscapingTry: isEscapingTry));
builder._current = null;
}
}
@@ -632,7 +640,7 @@
assert(isOpen);
return _continueWithExpression(
(k) => new ir.InvokeMethod(receiver, selector, mask, arguments, k,
- sourceInformation: sourceInformation));
+ sourceInformation));
}
ir.Primitive _buildInvokeCall(ir.Primitive target,
@@ -746,8 +754,7 @@
thenContinuation.body = thenBuilder._root;
elseContinuation.body = elseBuilder._root;
add(new ir.LetCont(join.continuation,
- new ir.LetCont.many(<ir.Continuation>[thenContinuation,
- elseContinuation],
+ new ir.LetCont.two(thenContinuation, elseContinuation,
new ir.Branch(new ir.IsTrue(condition),
thenContinuation,
elseContinuation))));
@@ -862,8 +869,15 @@
Selector selector,
TypeMask mask) {
assert(selector.isGetter);
- return _buildInvokeDynamic(
- receiver, selector, mask, const <ir.Primitive>[]);
+ FieldElement field = program.locateSingleField(selector, mask);
+ if (field != null) {
+ // If the world says this resolves to a unique field, then it MUST be
+ // treated as a field access, since the getter might not be emitted.
+ return buildFieldGet(receiver, field);
+ } else {
+ return _buildInvokeDynamic(
+ receiver, selector, mask, const <ir.Primitive>[]);
+ }
}
/// Create a dynamic setter invocation on [receiver] where the setter name and
@@ -873,7 +887,14 @@
TypeMask mask,
ir.Primitive value) {
assert(selector.isSetter);
- _buildInvokeDynamic(receiver, selector, mask, <ir.Primitive>[value]);
+ FieldElement field = program.locateSingleField(selector, mask);
+ if (field != null) {
+ // If the world says this resolves to a unique field, then it MUST be
+ // treated as a field access, since the setter might not be emitted.
+ buildFieldSet(receiver, field, value);
+ } else {
+ _buildInvokeDynamic(receiver, selector, mask, <ir.Primitive>[value]);
+ }
return value;
}
@@ -1223,8 +1244,7 @@
// Note the order of continuations: the first one is the one that will
// be filled by LetCont.plug.
ir.LetCont branch =
- new ir.LetCont.many(<ir.Continuation>[exitContinuation,
- bodyContinuation],
+ new ir.LetCont.two(exitContinuation, bodyContinuation,
new ir.Branch(new ir.IsTrue(condition),
bodyContinuation,
exitContinuation));
@@ -1384,8 +1404,7 @@
// Note the order of continuations: the first one is the one that will
// be filled by LetCont.plug.
ir.LetCont branch =
- new ir.LetCont.many(<ir.Continuation>[exitContinuation,
- bodyContinuation],
+ new ir.LetCont.two(exitContinuation, bodyContinuation,
new ir.Branch(new ir.IsTrue(condition),
bodyContinuation,
exitContinuation));
@@ -1460,8 +1479,7 @@
// Note the order of continuations: the first one is the one that will
// be filled by LetCont.plug.
ir.LetCont branch =
- new ir.LetCont.many(<ir.Continuation>[exitContinuation,
- bodyContinuation],
+ new ir.LetCont.two(exitContinuation, bodyContinuation,
new ir.Branch(new ir.IsTrue(condition),
bodyContinuation,
exitContinuation));
@@ -1547,8 +1565,7 @@
repeatContinuation.body = repeatBuilder._root;
continueBuilder.add(
- new ir.LetCont.many(<ir.Continuation>[exitContinuation,
- repeatContinuation],
+ new ir.LetCont.two(exitContinuation, repeatContinuation,
new ir.Branch(new ir.IsTrue(condition),
repeatContinuation,
exitContinuation)));
@@ -1614,8 +1631,7 @@
// continuation, to be plugged by the translation. Therefore put the
// else continuation first.
casesBuilder.add(
- new ir.LetCont.many(<ir.Continuation>[elseContinuation,
- thenContinuation],
+ new ir.LetCont.two(elseContinuation, thenContinuation,
new ir.Branch(new ir.IsTrue(condition),
thenContinuation,
elseContinuation)));
@@ -1820,8 +1836,7 @@
checkBuilder.buildTypeOperator(exceptionParameter,
clause.type,
isTypeTest: true);
- checkBuilder.add(new ir.LetCont.many([thenContinuation,
- elseContinuation],
+ checkBuilder.add(new ir.LetCont.two(thenContinuation, elseContinuation,
new ir.Branch(new ir.IsTrue(typeMatches),
thenContinuation,
elseContinuation)));
@@ -2092,8 +2107,7 @@
..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant]));
add(new ir.LetCont(joinContinuation,
- new ir.LetCont.many(<ir.Continuation>[thenContinuation,
- elseContinuation],
+ new ir.LetCont.two(thenContinuation, elseContinuation,
new ir.Branch(new ir.IsTrue(condition),
thenContinuation,
elseContinuation))));
@@ -2153,8 +2167,7 @@
rightFalseContinuation.body = rightFalseBuilder._root;
// The right subexpression has two continuations.
rightBuilder.add(
- new ir.LetCont.many(<ir.Continuation>[rightTrueContinuation,
- rightFalseContinuation],
+ new ir.LetCont.two(rightTrueContinuation, rightFalseContinuation,
new ir.Branch(new ir.IsTrue(rightValue),
rightTrueContinuation,
rightFalseContinuation)));
@@ -2170,8 +2183,7 @@
}
add(new ir.LetCont(join.continuation,
- new ir.LetCont.many(<ir.Continuation>[leftTrueContinuation,
- leftFalseContinuation],
+ new ir.LetCont.two(leftTrueContinuation, leftFalseContinuation,
new ir.Branch(new ir.IsTrue(leftValue),
leftTrueContinuation,
leftFalseContinuation))));
@@ -2397,6 +2409,16 @@
return state.thisParameter;
}
+ ir.Primitive buildFieldGet(ir.Primitive receiver, FieldElement target) {
+ return addPrimitive(new ir.GetField(receiver, target));
+ }
+
+ void buildFieldSet(ir.Primitive receiver,
+ FieldElement target,
+ ir.Primitive value) {
+ add(new ir.SetField(receiver, target, value));
+ }
+
ir.Primitive buildSuperFieldGet(FieldElement target) {
return addPrimitive(new ir.GetField(buildThis(), target));
}
@@ -2585,6 +2607,10 @@
}
return addPrimitive(new ir.TypeTest(value, type, typeArguments));
} else {
+ if (type.isObject || type.isDynamic) {
+ // `x as Object` and `x as dynamic` are the same as `x`.
+ return value;
+ }
return _continueWithExpression(
(k) => new ir.TypeCast(value, type, typeArguments, k));
}
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
index dd10751..8b81f39 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
@@ -43,7 +43,7 @@
/// This class is mainly there to correctly measure how long building the IR
/// takes.
class IrBuilderTask extends CompilerTask {
- final SourceInformationFactory sourceInformationFactory;
+ final SourceInformationStrategy sourceInformationStrategy;
String bailoutMessage = null;
@@ -51,7 +51,7 @@
/// [ir.FunctionDefinition] node that has been built.
IrBuilderCallback builderCallback;
- IrBuilderTask(Compiler compiler, this.sourceInformationFactory,
+ IrBuilderTask(Compiler compiler, this.sourceInformationStrategy,
[this.builderCallback])
: super(compiler);
@@ -65,7 +65,7 @@
element = element.implementation;
return compiler.withCurrentElement(element, () {
SourceInformationBuilder sourceInformationBuilder =
- sourceInformationFactory.forContext(element);
+ sourceInformationStrategy.createBuilderForContext(element);
IrBuilderVisitor builder =
new JsIrBuilderVisitor(
@@ -2187,6 +2187,10 @@
TypeMask getTypeMaskForForeign(NativeBehavior behavior) {
return TypeMaskFactory.fromNativeBehavior(behavior, _compiler);
}
+
+ FieldElement locateSingleField(Selector selector, TypeMask type) {
+ return _compiler.world.locateSingleField(selector, type);
+ }
}
/// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder].
@@ -3034,7 +3038,8 @@
element, CallStructure.TWO_ARGS);
return irBuilder.buildStaticFunctionInvocation(element,
CallStructure.TWO_ARGS, arguments,
- sourceInformation: sourceInformationBuilder.buildCall(node));
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, node.selector));
}
/// Lookup the value of the enum described by [node].
@@ -3181,14 +3186,18 @@
if (!compiler.hasIsolateSupport) {
// If the isolate library is not used, we just generate code
// to fetch the current isolate.
- String name = backend.namer.currentIsolate;
- return irBuilder.buildForeignCode(js.js.parseForeignJS(name),
- const <ir.Primitive>[], NativeBehavior.PURE);
- } else {
- return buildIsolateHelperInvocation('_currentIsolate',
- CallStructure.NO_ARGS);
+ continue GET_CURRENT_ISOLATE;
}
- break;
+ return buildIsolateHelperInvocation('_currentIsolate',
+ CallStructure.NO_ARGS);
+
+ GET_CURRENT_ISOLATE: case 'JS_CURRENT_ISOLATE':
+ validateArgumentCount(exactly: 0);
+
+ return irBuilder.buildForeignCode(
+ js.js.parseForeignJS(backend.namer.currentIsolate),
+ const <ir.Primitive>[],
+ NativeBehavior.PURE);
case 'JS_CALL_IN_ISOLATE':
validateArgumentCount(exactly: 2);
@@ -3197,11 +3206,9 @@
ir.Primitive closure = visit(argumentNodes.tail.head);
return irBuilder.buildCallInvocation(closure, CallStructure.NO_ARGS,
const <ir.Primitive>[]);
- } else {
- return buildIsolateHelperInvocation('_callInIsolate',
- CallStructure.TWO_ARGS);
}
- break;
+ return buildIsolateHelperInvocation('_callInIsolate',
+ CallStructure.TWO_ARGS);
default:
giveup(node, 'unplemented native construct: ${function.name}');
@@ -3220,7 +3227,8 @@
} else {
return irBuilder.buildStaticFunctionInvocation(function, callStructure,
translateStaticArguments(argumentList, function, callStructure),
- sourceInformation: sourceInformationBuilder.buildCall(node));
+ sourceInformation:
+ sourceInformationBuilder.buildCall(node, node.selector));
}
}
}
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
index fec259b..29d324e 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
@@ -3,13 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
library dart2js.ir_nodes;
-import '../constants/expressions.dart';
import '../constants/values.dart' as values show ConstantValue;
import '../dart_types.dart' show DartType, InterfaceType, TypeVariableType;
import '../elements/elements.dart';
import '../io/source_information.dart' show SourceInformation;
import '../types/types.dart' show TypeMask;
-import '../universe/universe.dart' show Selector, SelectorKind;
+import '../universe/universe.dart' show Selector;
import 'builtin_operator.dart';
export 'builtin_operator.dart';
@@ -19,7 +18,6 @@
// abstractions for native code and its type and effect system.
import '../js/js.dart' as js show Template;
import '../native/native.dart' as native show NativeBehavior;
-import '../types/types.dart' as types show TypeMask;
abstract class Node {
/// A pointer to the parent node. Is null until set by optimization passes.
@@ -97,7 +95,11 @@
///
/// False must be returned for primitives that may throw, diverge, or have
/// observable side-effects.
- bool get isSafeForElimination => true;
+ bool get isSafeForElimination;
+
+ /// True if time-of-evaluation is irrelevant for the given primitive,
+ /// assuming its inputs are the same values.
+ bool get isSafeForReordering;
}
/// Operands to invocations and primitives are always variables. They point to
@@ -169,6 +171,9 @@
LetCont(Continuation continuation, this.body)
: continuations = <Continuation>[continuation];
+ LetCont.two(Continuation first, Continuation second, this.body)
+ : continuations = <Continuation>[first, second];
+
LetCont.many(this.continuations, this.body);
Expression plug(Expression expr) {
@@ -223,9 +228,10 @@
accept(Visitor visitor) => visitor.visitLetMutable(this);
}
-abstract class Invoke {
+abstract class Invoke implements Expression {
Selector get selector;
List<Reference<Primitive>> get arguments;
+ Reference<Continuation> get continuation;
}
/// Represents a node with a child node, which can be accessed through the
@@ -260,10 +266,16 @@
this.selector,
List<Primitive> args,
Continuation cont,
- this.sourceInformation)
+ [this.sourceInformation])
: arguments = _referenceList(args),
continuation = new Reference<Continuation>(cont);
+ InvokeStatic.byReference(this.target,
+ this.selector,
+ this.arguments,
+ this.continuation,
+ [this.sourceInformation]);
+
accept(Visitor visitor) => visitor.visitInvokeStatic(this);
}
@@ -276,10 +288,6 @@
///
/// The [selector] records the names of named arguments. The value of named
/// arguments occur at the end of the [arguments] list, in normalized order.
-///
-/// Discussion:
-/// If the [selector] is a [TypedSelector], the type information contained
-/// there is used by optimization passes. This is likely to change.
class InvokeMethod extends Expression implements Invoke {
Reference<Primitive> receiver;
Selector selector;
@@ -296,11 +304,18 @@
this.mask,
List<Primitive> arguments,
Continuation continuation,
- {this.sourceInformation})
+ [this.sourceInformation])
: this.receiver = new Reference<Primitive>(receiver),
this.arguments = _referenceList(arguments),
this.continuation = new Reference<Continuation>(continuation);
+ InvokeMethod.byReference(this.receiver,
+ this.selector,
+ this.mask,
+ this.arguments,
+ this.continuation,
+ [this.sourceInformation]);
+
accept(Visitor visitor) => visitor.visitInvokeMethod(this);
}
@@ -406,6 +421,9 @@
this.typeArguments = _referenceList(typeArguments);
accept(Visitor visitor) => visitor.visitTypeTest(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// An "as" type cast.
@@ -448,6 +466,9 @@
: this.arguments = _referenceList(arguments);
accept(Visitor visitor) => visitor.visitApplyBuiltinOperator(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// Throw a value.
@@ -483,6 +504,9 @@
NonTailThrow(Primitive value) : value = new Reference<Primitive>(value);
accept(Visitor visitor) => visitor.visitNonTailThrow(this);
+
+ bool get isSafeForElimination => false;
+ bool get isSafeForReordering => false;
}
/// An expression that is known to be unreachable.
@@ -507,6 +531,9 @@
: this.variable = new Reference<MutableVariable>(variable);
accept(Visitor visitor) => visitor.visitGetMutableVariable(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => false;
}
/// Assign a [MutableVariable].
@@ -541,8 +568,13 @@
// the continuation itself.
bool isRecursive;
+ /// True if this invocation escapes from the body of a [LetHandler]
+ /// (i.e. a try block). Notably, such an invocation cannot be inlined.
+ bool isEscapingTry;
+
InvokeContinuation(Continuation cont, List<Primitive> args,
- {this.isRecursive: false})
+ {this.isRecursive: false,
+ this.isEscapingTry: false})
: continuation = new Reference<Continuation>(cont),
arguments = _referenceList(args) {
assert(cont.parameters == null || cont.parameters.length == args.length);
@@ -554,7 +586,8 @@
///
/// Used as a placeholder for a jump whose target is not yet created
/// (e.g., in the translation of break and continue).
- InvokeContinuation.uninitialized({this.isRecursive: false})
+ InvokeContinuation.uninitialized({this.isRecursive: false,
+ this.isEscapingTry: false})
: continuation = null,
arguments = null;
@@ -624,19 +657,28 @@
accept(Visitor visitor) => visitor.visitGetField(this);
- @override
bool get isSafeForElimination => objectIsNotNull;
+ bool get isSafeForReordering => objectIsNotNull && field.isFinal;
}
/// Reads the value of a static field or tears off a static method.
+///
+/// Note that lazily initialized fields should be read using GetLazyStatic.
class GetStatic extends Primitive {
/// Can be [FieldElement] or [FunctionElement].
final Element element;
final SourceInformation sourceInformation;
- GetStatic(this.element, this.sourceInformation);
+ GetStatic(this.element, [this.sourceInformation]);
accept(Visitor visitor) => visitor.visitGetStatic(this);
+
+ bool get isSafeForElimination {
+ return true;
+ }
+ bool get isSafeForReordering {
+ return element is FunctionElement || element.isFinal;
+ }
}
/// Sets the value of a static field.
@@ -646,7 +688,7 @@
Expression body;
final SourceInformation sourceInformation;
- SetStatic(this.element, Primitive value, this.sourceInformation)
+ SetStatic(this.element, Primitive value, [this.sourceInformation])
: this.value = new Reference<Primitive>(value);
Expression plug(Expression expr) {
@@ -670,7 +712,7 @@
GetLazyStatic(this.element,
Continuation continuation,
- this.sourceInformation)
+ [this.sourceInformation])
: continuation = new Reference<Continuation>(continuation);
accept(Visitor visitor) => visitor.visitGetLazyStatic(this);
@@ -679,6 +721,9 @@
/// Creates an object for holding boxed variables captured by a closure.
class CreateBox extends Primitive {
accept(Visitor visitor) => visitor.visitCreateBox(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// Creates an instance of a class and initializes its fields and runtime type
@@ -703,6 +748,9 @@
this.typeInformation = _referenceList(typeInformation);
accept(Visitor visitor) => visitor.visitCreateInstance(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
class Interceptor extends Primitive {
@@ -711,6 +759,9 @@
Interceptor(Primitive input, this.interceptedClasses)
: this.input = new Reference<Primitive>(input);
accept(Visitor visitor) => visitor.visitInterceptor(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// Create an instance of [Invocation] for use in a call to `noSuchMethod`.
@@ -722,11 +773,14 @@
: this.arguments = _referenceList(arguments);
accept(Visitor visitor) => visitor.visitCreateInvocationMirror(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
class ForeignCode extends Expression {
final js.Template codeTemplate;
- final types.TypeMask type;
+ final TypeMask type;
final List<Reference<Primitive>> arguments;
final native.NativeBehavior nativeBehavior;
final FunctionElement dependency;
@@ -750,6 +804,9 @@
Constant(this.value);
accept(Visitor visitor) => visitor.visitConstant(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
class LiteralList extends Primitive {
@@ -761,6 +818,9 @@
: this.values = _referenceList(values);
accept(Visitor visitor) => visitor.visitLiteralList(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
class LiteralMapEntry {
@@ -779,6 +839,9 @@
LiteralMap(this.type, this.entries);
accept(Visitor visitor) => visitor.visitLiteralMap(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// Currently unused.
@@ -797,6 +860,9 @@
CreateFunction(this.definition);
accept(Visitor visitor) => visitor.visitCreateFunction(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
class Parameter extends Primitive {
@@ -813,6 +879,9 @@
accept(Visitor visitor) => visitor.visitParameter(this);
String toString() => 'Parameter(${hint == null ? null : hint.name})';
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// Continuations are normally bound by 'let cont'. A continuation with one
@@ -834,7 +903,9 @@
Continuation(this.parameters, {this.isRecursive: false});
- Continuation.retrn() : parameters = <Parameter>[new Parameter(null)];
+ Continuation.retrn()
+ : parameters = <Parameter>[new Parameter(null)],
+ isRecursive = false;
accept(Visitor visitor) => visitor.visitContinuation(this);
}
@@ -879,6 +950,9 @@
@override
accept(Visitor visitor) => visitor.visitReifyRuntimeType(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// Read the value the type variable [variable] from the target object.
@@ -895,6 +969,9 @@
@override
accept(Visitor visitor) => visitor.visitReadTypeVariable(this);
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
/// Representation of a closed type (that is, a type without type variables).
@@ -917,6 +994,9 @@
accept(Visitor visitor) {
return visitor.visitTypeExpression(this);
}
+
+ bool get isSafeForElimination => true;
+ bool get isSafeForReordering => true;
}
List<Reference<Primitive>> _referenceList(Iterable<Primitive> definitions) {
diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
index f93f493..091ca53 100644
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
@@ -53,6 +53,10 @@
return mask.locateSingleElement(selector, mask, classWorld.compiler);
}
+ bool needsNoSuchMethodHandling(TypeMask mask, Selector selector) {
+ return mask.needsNoSuchMethodHandling(selector, classWorld);
+ }
+
TypeMask getReceiverType(MethodElement method) {
assert(method.isInstanceMember);
return nonNullSubclass(method.enclosingClass);
@@ -170,6 +174,15 @@
// TODO(asgerf): Support function types, and what else might be missing.
return AbstractBool.Maybe;
}
+
+ /// Returns whether [type] is one of the falsy values: false, 0, -0, NaN,
+ /// the empty string, or null.
+ AbstractBool boolify(TypeMask type) {
+ if (isDefinitelyNotNumStringBool(type) && !type.isNullable) {
+ return AbstractBool.True;
+ }
+ return AbstractBool.Maybe;
+ }
}
class ConstantPropagationLattice {
@@ -367,6 +380,30 @@
}
}
+ bool isEmptyString(ConstantValue value) {
+ return value is StringConstantValue && value.primitiveValue.isEmpty;
+ }
+
+ /// Returns whether [value] is one of the falsy values: false, 0, -0, NaN,
+ /// the empty string, or null.
+ AbstractBool boolify(AbstractValue value) {
+ if (value.isNothing) return AbstractBool.Nothing;
+ if (value.isConstant) {
+ ConstantValue constantValue = value.constant;
+ if (constantValue.isFalse ||
+ constantValue.isNull ||
+ constantValue.isZero ||
+ constantValue.isMinusZero ||
+ constantValue.isNaN ||
+ isEmptyString(constantValue)) {
+ return AbstractBool.False;
+ } else {
+ return AbstractBool.True;
+ }
+ }
+ return typeSystem.boolify(value.type);
+ }
+
/// The possible return types of a method that may be targeted by
/// [typedSelector]. If the given selector is not a [TypedSelector], any
/// reachable method matching the selector may be targeted.
@@ -426,8 +463,7 @@
TransformingVisitor transformer = new TransformingVisitor(
_compiler,
_lattice,
- analyzer.reachableNodes,
- analyzer.values,
+ analyzer,
replacements,
_internalError);
transformer.transform(root);
@@ -455,8 +491,7 @@
* actual transformations on the CPS graph.
*/
class TransformingVisitor extends RecursiveVisitor {
- final Set<Node> reachable;
- final Map<Node, AbstractValue> values;
+ final TypePropagationVisitor analyzer;
final Map<Expression, ConstantValue> replacements;
final ConstantPropagationLattice lattice;
final dart2js.Compiler compiler;
@@ -464,13 +499,14 @@
JavaScriptBackend get backend => compiler.backend;
TypeMaskSystem get typeSystem => lattice.typeSystem;
types.DartTypes get dartTypes => lattice.dartTypes;
+ Set<Node> get reachable => analyzer.reachableNodes;
+ Map<Node, AbstractValue> get values => analyzer.values;
final dart2js.InternalErrorFunction internalError;
TransformingVisitor(this.compiler,
this.lattice,
- this.reachable,
- this.values,
+ this.analyzer,
this.replacements,
this.internalError);
@@ -478,20 +514,34 @@
visit(root);
}
+ /// Sets parent pointers and computes types for the given subtree.
+ void reanalyze(Node node) {
+ new ParentVisitor().visit(node);
+ analyzer.reanalyzeSubtree(node);
+ }
+
/// Removes the entire subtree of [node] and inserts [replacement].
- /// All references in the [node] subtree are unlinked, and parent pointers
- /// in [replacement] are initialized.
+ ///
+ /// By default, all references in the [node] subtree are unlinked, and parent
+ /// pointers in [replacement] are initialized and its types recomputed.
+ ///
+ /// If the caller needs to manually unlink the node, because some references
+ /// were adopted by other nodes, it can be disabled by passing `false`
+ /// as the [unlink] parameter.
///
/// [replacement] must be "fresh", i.e. it must not contain significant parts
/// of the original IR inside of it since the [ParentVisitor] will
/// redundantly reprocess it.
- void replaceSubtree(Expression node, Expression replacement) {
+ void replaceSubtree(Expression node, Expression replacement,
+ {bool unlink: true}) {
InteriorNode parent = node.parent;
parent.body = replacement;
replacement.parent = parent;
node.parent = null;
- RemovalVisitor.remove(node);
- new ParentVisitor().visit(replacement);
+ if (unlink) {
+ RemovalVisitor.remove(node);
+ }
+ reanalyze(replacement);
}
/// Make a constant primitive for [constant] and set its entry in [values].
@@ -525,7 +575,8 @@
/// The new expression will be visited.
///
/// Returns true if the node was replaced.
- bool constifyExpression(Expression node, Continuation continuation) {
+ bool constifyExpression(Invoke node) {
+ Continuation continuation = node.continuation.definition;
ConstantValue constant = replacements[node];
if (constant == null) return false;
Constant primitive = makeConstantPrimitive(constant);
@@ -542,50 +593,55 @@
//
// (Branch (IsTrue true) k0 k1) -> (InvokeContinuation k0)
void visitBranch(Branch node) {
- bool trueReachable = reachable.contains(node.trueContinuation.definition);
- bool falseReachable = reachable.contains(node.falseContinuation.definition);
- bool bothReachable = (trueReachable && falseReachable);
- bool noneReachable = !(trueReachable || falseReachable);
+ Continuation trueCont = node.trueContinuation.definition;
+ Continuation falseCont = node.falseContinuation.definition;
+ IsTrue conditionNode = node.condition;
+ Primitive condition = conditionNode.value.definition;
- if (bothReachable || noneReachable) {
- // Nothing to do, shrinking reductions take care of the unreachable case.
- super.visitBranch(node);
+ AbstractValue conditionValue = getValue(condition);
+ AbstractBool boolifiedValue = lattice.boolify(conditionValue);
+
+ if (boolifiedValue == AbstractBool.True) {
+ InvokeContinuation invoke = new InvokeContinuation(trueCont, []);
+ replaceSubtree(node, invoke);
+ visitInvokeContinuation(invoke);
+ return;
+ }
+ if (boolifiedValue == AbstractBool.False) {
+ InvokeContinuation invoke = new InvokeContinuation(falseCont, []);
+ replaceSubtree(node, invoke);
+ visitInvokeContinuation(invoke);
return;
}
- Continuation successor = (trueReachable) ?
- node.trueContinuation.definition : node.falseContinuation.definition;
-
- // Replace the branch by a continuation invocation.
-
- assert(successor.parameters.isEmpty);
- InvokeContinuation invoke =
- new InvokeContinuation(successor, <Primitive>[]);
-
- replaceSubtree(node, invoke);
- visitInvokeContinuation(invoke);
- }
-
- /// True if the given reference is a use that converts its value to a boolean
- /// and only uses the coerced value.
- bool isBoolifyingUse(Reference<Primitive> ref) {
- Node use = ref.parent;
- return use is IsTrue ||
- use is ApplyBuiltinOperator && use.operator == BuiltinOperator.IsFalsy;
- }
-
- /// True if all uses of [prim] only use its value after boolean conversion.
- bool isAlwaysBoolified(Primitive prim) {
- for (Reference ref = prim.firstRef; ref != null; ref = ref.next) {
- if (!isBoolifyingUse(ref)) return false;
+ if (condition is ApplyBuiltinOperator &&
+ condition.operator == BuiltinOperator.LooseEq) {
+ Primitive leftArg = condition.arguments[0].definition;
+ Primitive rightArg = condition.arguments[1].definition;
+ AbstractValue left = getValue(leftArg);
+ AbstractValue right = getValue(rightArg);
+ if (right.isNullConstant &&
+ lattice.isDefinitelyNotNumStringBool(left)) {
+ // Rewrite:
+ // if (x == null) S1 else S2
+ // =>
+ // if (x) S2 else S1 (note the swapped branches)
+ Branch branch = new Branch(new IsTrue(leftArg), falseCont, trueCont);
+ replaceSubtree(node, branch);
+ return;
+ } else if (left.isNullConstant &&
+ lattice.isDefinitelyNotNumStringBool(right)) {
+ Branch branch = new Branch(new IsTrue(rightArg), falseCont, trueCont);
+ replaceSubtree(node, branch);
+ return;
+ }
}
- return true;
}
/// Replaces [node] with a more specialized instruction, if possible.
///
/// Returns `true` if the node was replaced.
- bool specializeInvoke(InvokeMethod node) {
+ bool specializeOperatorCall(InvokeMethod node) {
Continuation cont = node.continuation.definition;
bool replaceWithBinary(BuiltinOperator operator,
Primitive left,
@@ -597,15 +653,6 @@
visitLetPrim(let);
return true; // So returning early is more convenient.
}
- bool replaceWithUnary(BuiltinOperator operator,
- Primitive argument) {
- Primitive prim =
- new ApplyBuiltinOperator(operator, <Primitive>[argument]);
- LetPrim let = makeLetPrimInvoke(prim, cont);
- replaceSubtree(node, let);
- visitLetPrim(let);
- return true;
- }
if (node.selector.isOperator && node.arguments.length == 2) {
// The operators we specialize are are intercepted calls, so the operands
@@ -619,19 +666,6 @@
// Equality is special due to its treatment of null values and the
// fact that Dart-null corresponds to both JS-null and JS-undefined.
// Please see documentation for IsFalsy, StrictEq, and LooseEq.
- bool isBoolified = isAlwaysBoolified(cont.parameters.single);
- // Comparison with null constants.
- if (isBoolified &&
- right.isNullConstant &&
- lattice.isDefinitelyNotNumStringBool(left)) {
- // TODO(asgerf): This is shorter but might confuse te VM? Evaluate.
- return replaceWithUnary(BuiltinOperator.IsFalsy, leftArg);
- }
- if (isBoolified &&
- left.isNullConstant &&
- lattice.isDefinitelyNotNumStringBool(right)) {
- return replaceWithUnary(BuiltinOperator.IsFalsy, rightArg);
- }
if (left.isNullConstant || right.isNullConstant) {
return replaceWithBinary(BuiltinOperator.LooseEq, leftArg, rightArg);
}
@@ -664,8 +698,10 @@
}
else if (lattice.isDefinitelyString(left, allowNull: false) &&
lattice.isDefinitelyString(right, allowNull: false)) {
- return replaceWithBinary(BuiltinOperator.StringConcatenate,
- leftArg, rightArg);
+ if (node.selector.name == '+') {
+ return replaceWithBinary(BuiltinOperator.StringConcatenate,
+ leftArg, rightArg);
+ }
}
}
}
@@ -698,10 +734,9 @@
/// invocation with a direct access to a field.
///
/// Returns `true` if the node was replaced.
- bool inlineFieldAccess(InvokeMethod node) {
+ bool specializeFieldAccess(InvokeMethod node) {
if (!node.selector.isGetter && !node.selector.isSetter) return false;
AbstractValue receiver = getValue(getDartReceiver(node));
- if (receiver.isNothing) return false;
Element target =
typeSystem.locateSingleElement(receiver.type, node.selector);
if (target is! FieldElement) return false;
@@ -711,7 +746,6 @@
Continuation cont = node.continuation.definition;
if (node.selector.isGetter) {
GetField get = new GetField(getDartReceiver(node), target);
- get.objectIsNotNull = receiver.isDefinitelyNotNull;
LetPrim let = makeLetPrimInvoke(get, cont);
replaceSubtree(node, let);
visitLetPrim(let);
@@ -730,11 +764,148 @@
}
}
+ /// If [prim] is the parameter to a call continuation, returns the
+ /// corresponding call.
+ Invoke getInvocationWithResult(Primitive prim) {
+ if (prim is Parameter && prim.parent is Continuation) {
+ Continuation cont = prim.parent;
+ if (cont.hasExactlyOneUse) {
+ Node use = cont.firstRef.parent;
+ if (use is Invoke) {
+ return use;
+ }
+ }
+ }
+ return null;
+ }
+
+ /// True if any side effect immediately before [current] can safely be
+ /// postponed until immediately before [target].
+ ///
+ /// An expression `e` can be moved right before [target] if
+ /// `canPostponeSideEffects(e.body, target)` is true and no reference
+ /// falls out of scope.
+ ///
+ /// A more sophisticated analysis would track side-effect dependencies
+ /// between `e` and the expressions between `e` and the target.
+ bool canPostponeSideEffects(Expression current, Expression target) {
+ Expression exp = current;
+ while (exp != target) {
+ if (exp is LetPrim && exp.primitive.isSafeForReordering) {
+ LetPrim let = exp;
+ exp = let.body;
+ } else if (exp is LetCont) {
+ LetCont let = exp;
+ exp = let.body;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// Rewrites an invocation of a torn-off method into a method call directly
+ /// on the receiver. For example:
+ ///
+ /// obj.get$foo().call$<n>(<args>)
+ /// =>
+ /// obj.foo$<n>(<args>)
+ ///
+ bool specializeClosureCall(InvokeMethod node) {
+ Selector call = node.selector;
+ if (!call.isClosureCall) return false;
+
+ assert(!isInterceptedSelector(call));
+ assert(call.argumentCount == node.arguments.length);
+
+ Primitive tearOff = node.receiver.definition;
+ // Note: We don't know if [tearOff] is actually a tear-off.
+ // We name variables based on the pattern we are trying to match.
+
+ if (tearOff is GetStatic && tearOff.element.isFunction) {
+ FunctionElement target = tearOff.element;
+ FunctionSignature signature = target.functionSignature;
+
+ // If the selector does not apply, don't bother (will throw at runtime).
+ if (!call.signatureApplies(target)) return false;
+
+ // If some optional arguments are missing, give up.
+ // TODO(asgerf): Improve optimization by inserting default arguments.
+ if (call.argumentCount != signature.parameterCount) return false;
+
+ InvokeStatic invoke = new InvokeStatic.byReference(
+ target,
+ new Selector.fromElement(target),
+ node.arguments,
+ node.continuation,
+ node.sourceInformation);
+ node.receiver.unlink();
+ replaceSubtree(node, invoke, unlink: false);
+ visitInvokeStatic(invoke);
+ return true;
+ }
+ Invoke tearOffInvoke = getInvocationWithResult(tearOff);
+ if (tearOffInvoke is InvokeMethod && tearOffInvoke.selector.isGetter) {
+ Selector getter = tearOffInvoke.selector;
+ Continuation getterCont = tearOffInvoke.continuation.definition;
+
+ // TODO(asgerf): Support torn-off intercepted methods.
+ if (isInterceptedSelector(getter)) return false;
+
+ Primitive object = tearOffInvoke.receiver.definition;
+
+ // Ensure that the object actually has a foo member, since we might
+ // otherwise alter a noSuchMethod call.
+ TypeMask type = getValue(object).type;
+ if (typeSystem.needsNoSuchMethodHandling(type, getter)) return false;
+
+ // Determine if the getter invocation can have side-effects.
+ Element element = typeSystem.locateSingleElement(type, getter);
+ bool isPure = element != null && !element.isGetter;
+
+ // If there are multiple uses, we cannot eliminate the getter call and
+ // therefore risk duplicating its side effects.
+ if (!isPure && tearOff.hasMultipleUses) return false;
+
+ // If the getter call is impure, we risk reordering side effects.
+ if (!isPure && !canPostponeSideEffects(getterCont.body, node)) {
+ return false;
+ }
+
+ InvokeMethod invoke = new InvokeMethod.byReference(
+ new Reference<Primitive>(object),
+ new Selector(SelectorKind.CALL, getter.memberName, call.callStructure),
+ type,
+ node.arguments,
+ node.continuation,
+ node.sourceInformation);
+ node.receiver.unlink();
+ replaceSubtree(node, invoke, unlink: false);
+
+ if (tearOff.hasNoUses) {
+ // Eliminate the getter call if it has no more uses.
+ // This cannot be delegated to other optimizations because we need to
+ // avoid duplication of side effects.
+ getterCont.parameters.clear();
+ replaceSubtree(tearOffInvoke, new InvokeContinuation(getterCont, []));
+ } else {
+ // There are more uses, so we cannot eliminate the getter call. This
+ // means we duplicated the effects of the getter call, but we should
+ // only get here if the getter has no side effects.
+ assert(isPure);
+ }
+
+ visitInvokeMethod(invoke);
+ return true;
+ }
+ return false;
+ }
+
void visitInvokeMethod(InvokeMethod node) {
- Continuation cont = node.continuation.definition;
- if (constifyExpression(node, cont)) return;
- if (specializeInvoke(node)) return;
- if (inlineFieldAccess(node)) return;
+ if (constifyExpression(node)) return;
+ if (specializeOperatorCall(node)) return;
+ if (specializeFieldAccess(node)) return;
+ if (specializeClosureCall(node)) return;
AbstractValue receiver = getValue(node.receiver.definition);
node.receiverIsNotNull = receiver.isDefinitelyNotNull;
@@ -768,10 +939,10 @@
super.visitTypeCast(node);
}
- /// Specialize calls to static methods.
+ /// Specialize calls to internal static methods.
///
/// Returns true if the call was replaced.
- bool specializeInvokeStatic(InvokeStatic node) {
+ bool specializeInternalMethodCall(InvokeStatic node) {
// TODO(asgerf): This is written to easily scale to more cases,
// either add more cases or clean up.
Continuation cont = node.continuation.definition;
@@ -794,8 +965,8 @@
}
void visitInvokeStatic(InvokeStatic node) {
- if (constifyExpression(node, node.continuation.definition)) return;
- if (specializeInvokeStatic(node)) return;
+ if (constifyExpression(node)) return;
+ if (specializeInternalMethodCall(node)) return;
}
AbstractValue getValue(Primitive primitive) {
@@ -848,6 +1019,7 @@
node.arguments[k] = null; // Remove the argument after the loop.
}
node.arguments[startOfSequence] = new Reference<Primitive>(prim);
+ node.arguments[startOfSequence].parent = node;
argumentsWereRemoved = true;
}
if (argumentsWereRemoved) {
@@ -911,16 +1083,15 @@
newPrim.substituteFor(node.primitive);
RemovalVisitor.remove(node.primitive);
node.primitive = newPrim;
+ newPrim.parent = node;
} else {
Primitive newPrim = visit(node.primitive);
if (newPrim != null) {
- if (!values.containsKey(newPrim)) {
- // If the type was not set, default to the same type as before.
- values[newPrim] = values[node.primitive];
- }
newPrim.substituteFor(node.primitive);
RemovalVisitor.remove(node.primitive);
node.primitive = newPrim;
+ newPrim.parent = node;
+ reanalyze(newPrim);
}
if (node.primitive.hasNoUses && node.primitive.isSafeForElimination) {
// Remove unused primitives before entering the body.
@@ -934,6 +1105,41 @@
}
visit(node.body);
}
+
+ void visitLetCont(LetCont node) {
+ // Visit body before continuations to ensure more information is available
+ // about the parameters. In particular, if this is a call continuation, its
+ // invocation should be specialized before the body is processed, because
+ // we specialize definitions before their uses.
+ visit(node.body);
+ node.continuations.forEach(visit);
+ }
+
+ void visitInvokeContinuation(InvokeContinuation node) {
+ // Inline the single-use continuations. These are often introduced when
+ // specializing an invocation node. These would also be inlined by a later
+ // pass, but doing it here helps simplify pattern matching code, since the
+ // effective definition of a primitive can then be found without going
+ // through redundant InvokeContinuations.
+ Continuation cont = node.continuation.definition;
+ if (cont.hasExactlyOneUse &&
+ !cont.isReturnContinuation &&
+ !cont.isRecursive &&
+ !node.isEscapingTry) {
+ for (int i = 0; i < node.arguments.length; ++i) {
+ node.arguments[i].definition.substituteFor(cont.parameters[i]);
+ node.arguments[i].unlink();
+ }
+ node.continuation.unlink();
+ InteriorNode parent = node.parent;
+ Expression body = cont.body;
+ parent.body = body;
+ body.parent = parent;
+ cont.body = new Unreachable();
+ cont.body.parent = cont;
+ visit(body);
+ }
+ }
}
/**
@@ -986,12 +1192,20 @@
void analyze(FunctionDefinition root) {
reachableNodes.clear();
- defWorkset.clear();
- nodeWorklist.clear();
// Initially, only the root node is reachable.
setReachable(root);
+ iterateWorklist();
+ }
+
+ void reanalyzeSubtree(Node node) {
+ new ResetAnalysisInfo(reachableNodes, values).visit(node);
+ setReachable(node);
+ iterateWorklist();
+ }
+
+ void iterateWorklist() {
while (true) {
if (nodeWorklist.isNotEmpty) {
// Process a new reachable expression.
@@ -1195,8 +1409,6 @@
}
void visitApplyBuiltinOperator(ApplyBuiltinOperator node) {
- // Note that most built-in operators do not exist before the transformation
- // pass after this analysis has finished.
switch (node.operator) {
case BuiltinOperator.StringConcatenate:
DartString stringValue = const LiteralDartString('');
@@ -1252,8 +1464,31 @@
}
break;
- default:
- setValue(node, nonConstant());
+ // TODO(asgerf): Implement constant propagation for builtins.
+ case BuiltinOperator.NumAdd:
+ case BuiltinOperator.NumSubtract:
+ case BuiltinOperator.NumMultiply:
+ case BuiltinOperator.NumAnd:
+ case BuiltinOperator.NumOr:
+ case BuiltinOperator.NumXor:
+ setValue(node, nonConstant(typeSystem.numType));
+ break;
+
+ case BuiltinOperator.NumLt:
+ case BuiltinOperator.NumLe:
+ case BuiltinOperator.NumGt:
+ case BuiltinOperator.NumGe:
+ case BuiltinOperator.StrictEq:
+ case BuiltinOperator.StrictNeq:
+ case BuiltinOperator.LooseEq:
+ case BuiltinOperator.LooseNeq:
+ case BuiltinOperator.IsFalsy:
+ case BuiltinOperator.IsNumber:
+ case BuiltinOperator.IsNotNumber:
+ case BuiltinOperator.IsFloor:
+ case BuiltinOperator.IsNumberAndFloor:
+ setValue(node, nonConstant(typeSystem.boolType));
+ break;
}
}
@@ -1375,7 +1610,12 @@
void visitConstant(Constant node) {
ConstantValue value = node.value;
- setValue(node, constantValue(value, typeSystem.getTypeOf(value)));
+ if (value.isDummy || !value.isConstant) {
+ // TODO(asgerf): Explain how this happens and why we don't want them.
+ setValue(node, nonConstant(typeSystem.getTypeOf(value)));
+ } else {
+ setValue(node, constantValue(value, typeSystem.getTypeOf(value)));
+ }
}
void visitCreateFunction(CreateFunction node) {
@@ -1538,10 +1778,11 @@
AbstractValue._internal(this.kind, this.constant, this.type) {
assert(kind != CONSTANT || constant != null);
+ assert(constant is! SyntheticConstantValue);
}
AbstractValue.nothing()
- : this._internal(NOTHING, null, null);
+ : this._internal(NOTHING, null, new TypeMask.nonNullEmpty());
AbstractValue.constantValue(ConstantValue constant, TypeMask type)
: this._internal(CONSTANT, constant, type);
@@ -1583,3 +1824,16 @@
abstract class InternalMethod {
static const String Stringify = 'S';
}
+
+class ResetAnalysisInfo extends RecursiveVisitor {
+ Set<Node> reachableNodes;
+ Map<Definition, AbstractValue> values;
+
+ ResetAnalysisInfo(this.reachableNodes, this.values);
+
+ visit(Node node) {
+ reachableNodes.remove(node);
+ if (node is Definition) values.remove(node);
+ node.accept(this);
+ }
+}
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 3b351af..2cbaf31 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -311,6 +311,7 @@
'--output-type=dart|--output-type=dart-multi|--output-type=js',
setOutputType),
new OptionHandler('--use-cps-ir', passThrough),
+ new OptionHandler('--no-frequency-based-minification', passThrough),
new OptionHandler('--verbose', setVerbose),
new OptionHandler('--version', (_) => wantVersion = true),
new OptionHandler('--library-root=.+', setLibraryRoot),
@@ -648,6 +649,10 @@
--use-cps-ir
Experimental. Use the new CPS based backend for code generation.
+
+ --no-frequency-based-minification
+ Experimental. Disabled the new frequency based minifying namer and use the
+ old namer instead.
'''.trim());
}
diff --git a/pkg/compiler/lib/src/dart2jslib.dart b/pkg/compiler/lib/src/dart2jslib.dart
index a203791..fcbeecb 100644
--- a/pkg/compiler/lib/src/dart2jslib.dart
+++ b/pkg/compiler/lib/src/dart2jslib.dart
@@ -52,6 +52,7 @@
import 'resolution/send_structure.dart';
import 'resolution/operators.dart' as op;
import 'scanner/scannerlib.dart';
+import 'serialization/task.dart';
import 'ssa/ssa.dart';
import 'io/source_file.dart' show SourceFile;
import 'tracer.dart' show Tracer;
diff --git a/pkg/compiler/lib/src/dart_types.dart b/pkg/compiler/lib/src/dart_types.dart
index 21fbc78..17e6472 100644
--- a/pkg/compiler/lib/src/dart_types.dart
+++ b/pkg/compiler/lib/src/dart_types.dart
@@ -9,30 +9,22 @@
import 'core_types.dart';
import 'dart2jslib.dart' show Compiler, invariant, Script, Message;
import 'elements/modelx.dart'
- show VoidElementX,
- LibraryElementX,
- BaseClassElementX,
+ show LibraryElementX,
TypeDeclarationElementX,
TypedefElementX;
import 'elements/elements.dart';
import 'ordered_typeset.dart' show OrderedTypeSet;
import 'util/util.dart' show CURRENT_ELEMENT_SPANNABLE, equalElements;
-class TypeKind {
- final String id;
-
- const TypeKind(String this.id);
-
- static const TypeKind FUNCTION = const TypeKind('function');
- static const TypeKind INTERFACE = const TypeKind('interface');
- static const TypeKind STATEMENT = const TypeKind('statement');
- static const TypeKind TYPEDEF = const TypeKind('typedef');
- static const TypeKind TYPE_VARIABLE = const TypeKind('type variable');
- static const TypeKind MALFORMED_TYPE = const TypeKind('malformed');
- static const TypeKind DYNAMIC = const TypeKind('dynamic');
- static const TypeKind VOID = const TypeKind('void');
-
- String toString() => id;
+enum TypeKind {
+ FUNCTION,
+ INTERFACE,
+ STATEMENT,
+ TYPEDEF,
+ TYPE_VARIABLE,
+ MALFORMED_TYPE,
+ DYNAMIC,
+ VOID,
}
abstract class DartType {
@@ -451,9 +443,9 @@
assert(invariant(element, element.isDeclaration));
}
- InterfaceType.forUserProvidedBadType(BaseClassElementX element,
- [List<DartType> typeArguments =
- const <DartType>[]])
+ InterfaceType.forUserProvidedBadType(
+ ClassElement element,
+ [List<DartType> typeArguments = const <DartType>[]])
: super(element, typeArguments, checkTypeArgumentCount: false);
ClassElement get element => super.element;
diff --git a/pkg/compiler/lib/src/elements/common.dart b/pkg/compiler/lib/src/elements/common.dart
index 8c8c4ff..f9013c6 100644
--- a/pkg/compiler/lib/src/elements/common.dart
+++ b/pkg/compiler/lib/src/elements/common.dart
@@ -126,6 +126,28 @@
@override
bool get isInternalLibrary =>
isPlatformLibrary && canonicalUri.path.startsWith('_');
+
+ String getLibraryOrScriptName() {
+ if (hasLibraryName()) {
+ return getLibraryName();
+ } else {
+ // Use the file name as script name.
+ String path = canonicalUri.path;
+ return path.substring(path.lastIndexOf('/') + 1);
+ }
+ }
+
+ int compareTo(LibraryElement other) {
+ if (this == other) return 0;
+ return getLibraryOrScriptName().compareTo(other.getLibraryOrScriptName());
+ }
+}
+
+abstract class CompilationUnitElementCommon implements CompilationUnitElement {
+ int compareTo(CompilationUnitElement other) {
+ if (this == other) return 0;
+ return '${script.readableUri}'.compareTo('${other.script.readableUri}');
+ }
}
abstract class ClassElementCommon implements ClassElement {
diff --git a/pkg/compiler/lib/src/elements/elements.dart b/pkg/compiler/lib/src/elements/elements.dart
index 22a0933..2f302b2 100644
--- a/pkg/compiler/lib/src/elements/elements.dart
+++ b/pkg/compiler/lib/src/elements/elements.dart
@@ -836,10 +836,8 @@
get enclosingElement;
Script get script;
- PartOf get partTag;
void forEachLocalMember(f(Element element));
- bool get hasMembers;
int compareTo(CompilationUnitElement other);
}
@@ -902,6 +900,15 @@
bool hasLibraryName();
String getLibraryName();
+
+ /**
+ * Returns the library name (as defined by the library tag) or for script
+ * (which have no library tag) the script file name. The latter case is used
+ * to provide a 'library name' for scripts to use for instance in dartdoc.
+ *
+ * Note: the returned filename is still escaped ("a%20b.dart" instead of
+ * "a b.dart").
+ */
String getLibraryOrScriptName();
int compareTo(LibraryElement other);
@@ -976,6 +983,9 @@
/// A top level, static or instance field, a formal parameter or local variable.
abstract class VariableElement extends ExecutableElement {
+ @override
+ VariableDefinitions get node;
+
Expression get initializer;
/// The constant expression defining the value of the variable if `const`,
@@ -1490,6 +1500,9 @@
/// The class or typedef on which this type variable is defined.
TypeDeclarationElement get typeDeclaration;
+ /// The index of this type variable within its type declaration.
+ int get index;
+
/// The [type] defined by the type variable.
TypeVariableType get type;
diff --git a/pkg/compiler/lib/src/elements/modelx.dart b/pkg/compiler/lib/src/elements/modelx.dart
index fc1f93b..1c5f782 100644
--- a/pkg/compiler/lib/src/elements/modelx.dart
+++ b/pkg/compiler/lib/src/elements/modelx.dart
@@ -652,6 +652,7 @@
}
class CompilationUnitElementX extends ElementX
+ with CompilationUnitElementCommon
implements CompilationUnitElement {
final Script script;
PartOf partTag;
@@ -718,11 +719,6 @@
bool get hasMembers => !localMembers.isEmpty;
- int compareTo(CompilationUnitElement other) {
- if (this == other) return 0;
- return '${script.readableUri}'.compareTo('${other.script.readableUri}');
- }
-
Element get analyzableElement => library;
accept(ElementVisitor visitor, arg) {
@@ -1070,24 +1066,6 @@
return libraryTag.name.toString();
}
- /**
- * Returns the library name (as defined by the library tag) or for script
- * (which have no library tag) the script file name. The latter case is used
- * to private 'library name' for scripts to use for instance in dartdoc.
- *
- * Note: the returned filename will still be escaped ("a%20b.dart" instead of
- * "a b.dart").
- */
- String getLibraryOrScriptName() {
- if (libraryTag != null) {
- return libraryTag.name.toString();
- } else {
- // Use the file name as script name.
- String path = canonicalUri.path;
- return path.substring(path.lastIndexOf('/') + 1);
- }
- }
-
Scope buildScope() => new LibraryScope(this);
String toString() {
@@ -1100,11 +1078,6 @@
}
}
- int compareTo(LibraryElement other) {
- if (this == other) return 0;
- return getLibraryOrScriptName().compareTo(other.getLibraryOrScriptName());
- }
-
accept(ElementVisitor visitor, arg) {
return visitor.visitLibraryElement(this, arg);
}
@@ -2194,7 +2167,7 @@
}
accept(ElementVisitor visitor, arg) {
- return visitor.visitFunctionElement(this, arg);
+ return visitor.visitConstructorElement(this, arg);
}
}
@@ -2275,12 +2248,12 @@
// Create types and elements for type variable.
Link<Node> nodes = parameters.nodes;
List<DartType> arguments =
- new List.generate(nodes.slowLength(), (_) {
+ new List.generate(nodes.slowLength(), (int index) {
TypeVariable node = nodes.head;
String variableName = node.name.source;
nodes = nodes.tail;
TypeVariableElementX variableElement =
- new TypeVariableElementX(variableName, this, node);
+ new TypeVariableElementX(variableName, this, index, node);
TypeVariableType variableType = new TypeVariableType(variableElement);
variableElement.typeCache = variableType;
return variableType;
@@ -2760,11 +2733,15 @@
class TypeVariableElementX extends ElementX with AstElementMixin
implements TypeVariableElement {
+ final int index;
final Node node;
TypeVariableType typeCache;
DartType boundCache;
- TypeVariableElementX(String name, TypeDeclarationElement enclosing, this.node)
+ TypeVariableElementX(String name,
+ TypeDeclarationElement enclosing,
+ this.index,
+ this.node)
: super(name, ElementKind.TYPE_VARIABLE, enclosing);
TypeDeclarationElement get typeDeclaration => enclosingElement;
diff --git a/pkg/compiler/lib/src/enqueue.dart b/pkg/compiler/lib/src/enqueue.dart
index 39f08dc..9325653 100644
--- a/pkg/compiler/lib/src/enqueue.dart
+++ b/pkg/compiler/lib/src/enqueue.dart
@@ -259,7 +259,7 @@
recentClasses.add(cls);
cls.ensureResolved(compiler);
cls.implementation.forEachMember(processInstantiatedClassMember);
- if (isResolutionQueue) {
+ if (isResolutionQueue && !cls.isSynthesized) {
compiler.resolver.checkClass(cls);
}
// We only tell the backend once that [cls] was instantiated, so
@@ -724,6 +724,7 @@
bool internalAddToWorkList(Element element) {
if (element.isErroneous) return false;
+
assert(invariant(element, element is AnalyzableElement,
message: 'Element $element is not analyzable.'));
if (hasBeenResolved(element)) return false;
@@ -734,7 +735,15 @@
compiler.world.registerUsedElement(element);
- queue.add(new ResolutionWorkItem(element, itemCompilationContextCreator()));
+ ResolutionWorkItem workItem;
+ if (compiler.serialization.isDeserialized(element)) {
+ workItem = compiler.serialization.createResolutionWorkItem(
+ element, itemCompilationContextCreator());
+ } else {
+ workItem = new ResolutionWorkItem(
+ element, itemCompilationContextCreator());
+ }
+ queue.add(workItem);
// Enable isolate support if we start using something from the isolate
// library, or timers for the async library. We exclude constant fields,
diff --git a/pkg/compiler/lib/src/io/code_output.dart b/pkg/compiler/lib/src/io/code_output.dart
index 1b4075d..967de65 100644
--- a/pkg/compiler/lib/src/io/code_output.dart
+++ b/pkg/compiler/lib/src/io/code_output.dart
@@ -8,12 +8,26 @@
import 'source_information.dart';
+/// Listener interface for [CodeOutput] activity.
abstract class CodeOutputListener {
+ /// Called when [text] is added to the output.
void onText(String text);
+
+ /// Called when the output is closed with a final length of [length].
void onDone(int length);
}
-abstract class CodeOutput {
+/// Interface for a mapping of target offsets to source locations.
+abstract class SourceLocations {
+ /// Adds a [sourceLocation] at the specified [targetOffset].
+ void addSourceLocation(int targetOffset, SourceLocation sourcePosition);
+
+ /// Applies [f] to every target offset and associated source location.
+ void forEachSourceLocation(void f(int targetOffset,
+ SourceLocation sourceLocation));
+}
+
+abstract class CodeOutput implements SourceLocations {
/// Write [text] to this output.
///
/// If the output is closed, a [StateError] is thrown.
@@ -33,13 +47,6 @@
/// Closes the output. Further writes will cause a [StateError].
void close();
-
- /// Adds a [sourceLocation] at the specified [targetOffset] in the buffer.
- void addSourceLocation(int targetOffset, SourceLocation sourcePosition);
-
- /// Applies [f] to every marker in this output.
- void forEachSourceLocation(void f(int targetOffset,
- SourceLocation sourceLocation));
}
abstract class AbstractCodeOutput extends CodeOutput {
diff --git a/pkg/compiler/lib/src/io/line_column_provider.dart b/pkg/compiler/lib/src/io/line_column_provider.dart
index fb3d5db..e8665fb 100644
--- a/pkg/compiler/lib/src/io/line_column_provider.dart
+++ b/pkg/compiler/lib/src/io/line_column_provider.dart
@@ -13,6 +13,9 @@
/// Returns the column number (0-based) for [offset] at the given [line].
int getColumn(int line, int offset);
+
+ /// Returns the offset for 0-based [line] and [column] numbers.
+ int getOffset(int line, int column);
}
/// [CodeOutputListener] that collects line information.
@@ -65,6 +68,8 @@
return offset - lineStarts[line];
}
+ int getOffset(int line, int column) => lineStarts[line] + column;
+
@override
void onDone(int length) {
lineStarts.add(length + 1);
diff --git a/pkg/compiler/lib/src/io/position_information.dart b/pkg/compiler/lib/src/io/position_information.dart
new file mode 100644
index 0000000..9b91cab1
--- /dev/null
+++ b/pkg/compiler/lib/src/io/position_information.dart
@@ -0,0 +1,439 @@
+// Copyright (c) 2015, 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.
+
+/// Source information system mapping that attempts a semantic mapping between
+/// offsets of JavaScript code points to offsets of Dart code points.
+
+library dart2js.source_information.position;
+
+import '../dart2jslib.dart' show
+ invariant,
+ MessageKind,
+ SourceSpan;
+import '../elements/elements.dart' show
+ AstElement,
+ LocalElement;
+import '../js/js.dart' as js;
+import '../js/js_source_mapping.dart';
+import '../js/js_debug.dart';
+import '../tree/tree.dart' show Node, Send;
+import '../util/util.dart' show NO_LOCATION_SPANNABLE;
+
+import 'source_file.dart';
+import 'source_information.dart';
+
+/// [SourceInformation] that consists of an offset position into the source
+/// code.
+class PositionSourceInformation extends SourceInformation {
+ @override
+ final SourceLocation startPosition;
+
+ @override
+ final SourceLocation closingPosition;
+
+ PositionSourceInformation(this.startPosition,
+ [this.closingPosition]);
+
+ @override
+ List<SourceLocation> get sourceLocations {
+ List<SourceLocation> list = <SourceLocation>[];
+ if (startPosition != null) {
+ list.add(startPosition);
+ }
+ if (closingPosition != null) {
+ list.add(closingPosition);
+ }
+ return list;
+ }
+
+ @override
+ SourceSpan get sourceSpan {
+ SourceLocation location =
+ startPosition != null ? startPosition : closingPosition;
+ Uri uri = location.sourceUri;
+ int offset = location.offset;
+ return new SourceSpan(uri, offset, offset);
+ }
+
+ int get hashCode {
+ return 0x7FFFFFFF &
+ (startPosition.hashCode * 17 + closingPosition.hashCode * 19);
+ }
+
+ bool operator ==(other) {
+ if (identical(this, other)) return true;
+ if (other is! PositionSourceInformation) return false;
+ return startPosition == other.startPosition &&
+ closingPosition == other.closingPosition;
+ }
+
+ /// Create a textual representation of the source information using [uriText]
+ /// as the Uri representation.
+ String _computeText(String uriText) {
+ StringBuffer sb = new StringBuffer();
+ sb.write('$uriText:');
+ // Use 1-based line/column info to match usual dart tool output.
+ if (startPosition != null) {
+ sb.write('[${startPosition.line + 1},'
+ '${startPosition.column + 1}]');
+ }
+ if (closingPosition != null) {
+ sb.write('-[${closingPosition.line + 1},'
+ '${closingPosition.column + 1}]');
+ }
+ return sb.toString();
+ }
+
+ String get shortText {
+ if (startPosition != null) {
+ return _computeText(startPosition.sourceUri.pathSegments.last);
+ } else {
+ return _computeText(closingPosition.sourceUri.pathSegments.last);
+ }
+ }
+
+ String toString() {
+ if (startPosition != null) {
+ return _computeText('${startPosition.sourceUri}');
+ } else {
+ return _computeText('${closingPosition.sourceUri}');
+ }
+ }
+}
+
+class PositionSourceInformationStrategy
+ implements JavaScriptSourceInformationStrategy {
+ const PositionSourceInformationStrategy();
+
+ @override
+ SourceInformationBuilder createBuilderForContext(AstElement element) {
+ return new PositionSourceInformationBuilder(element);
+ }
+
+ @override
+ SourceInformationProcessor createProcessor(SourceMapper mapper) {
+ return new PositionSourceInformationProcessor(mapper);
+ }
+}
+
+/// [SourceInformationBuilder] that generates [PositionSourceInformation].
+class PositionSourceInformationBuilder implements SourceInformationBuilder {
+ final SourceFile sourceFile;
+ final String name;
+
+ PositionSourceInformationBuilder(AstElement element)
+ : sourceFile = element.implementation.compilationUnit.script.file,
+ name = computeElementNameForSourceMaps(element);
+
+ SourceInformation buildDeclaration(AstElement element) {
+ if (element.isSynthesized) {
+ return new PositionSourceInformation(
+ new OffsetSourceLocation(
+ sourceFile, element.position.charOffset, name));
+ } else {
+ return new PositionSourceInformation(
+ null,
+ new OffsetSourceLocation(sourceFile,
+ element.resolvedAst.node.getEndToken().charOffset, name));
+ }
+ }
+
+ /// Builds a source information object pointing the start position of [node].
+ SourceInformation buildBegin(Node node) {
+ return new PositionSourceInformation(new OffsetSourceLocation(
+ sourceFile, node.getBeginToken().charOffset, name));
+ }
+
+ @override
+ SourceInformation buildGeneric(Node node) => buildBegin(node);
+
+ @override
+ SourceInformation buildReturn(Node node) => buildBegin(node);
+
+ @override
+ SourceInformation buildImplicitReturn(AstElement element) {
+ if (element.isSynthesized) {
+ return new PositionSourceInformation(
+ new OffsetSourceLocation(
+ sourceFile, element.position.charOffset, name));
+ } else {
+ return new PositionSourceInformation(
+ new OffsetSourceLocation(sourceFile,
+ element.resolvedAst.node.getEndToken().charOffset, name));
+ }
+ }
+
+
+ @override
+ SourceInformation buildLoop(Node node) => buildBegin(node);
+
+ @override
+ SourceInformation buildGet(Node node) => buildBegin(node);
+
+ @override
+ SourceInformation buildCall(Node receiver, Node call) {
+ return new PositionSourceInformation(
+ new OffsetSourceLocation(
+ sourceFile, receiver.getBeginToken().charOffset, name),
+ new OffsetSourceLocation(
+ sourceFile, call.getBeginToken().charOffset, name));
+ }
+
+ @override
+ SourceInformation buildNew(Node node) {
+ return buildBegin(node);
+ }
+
+ @override
+ SourceInformation buildIf(Node node) => buildBegin(node);
+
+ @override
+ SourceInformation buildThrow(Node node) => buildBegin(node);
+
+ @override
+ SourceInformation buildAssignment(Node node) => buildBegin(node);
+
+ @override
+ SourceInformationBuilder forContext(AstElement element) {
+ return new PositionSourceInformationBuilder(element);
+ }
+}
+
+/// The start, end and closing offsets for a [js.Node].
+class CodePosition {
+ final int startPosition;
+ final int endPosition;
+ final int closingPosition;
+
+ CodePosition(this.startPosition, this.endPosition, this.closingPosition);
+}
+
+/// Registry for mapping [js.Node]s to their [CodePosition].
+class CodePositionRecorder {
+ Map<js.Node, CodePosition> _codePositionMap = <js.Node, CodePosition>{};
+
+ void registerPositions(js.Node node,
+ int startPosition,
+ int endPosition,
+ int closingPosition) {
+ registerCodePosition(node,
+ new CodePosition(startPosition, endPosition, closingPosition));
+ }
+
+ void registerCodePosition(js.Node node, CodePosition codePosition) {
+ _codePositionMap[node] = codePosition;
+ }
+
+ CodePosition operator [](js.Node node) => _codePositionMap[node];
+}
+
+enum SourcePositionKind {
+ START,
+ CLOSING,
+ END,
+}
+
+enum CodePositionKind {
+ START,
+ CLOSING,
+ END,
+}
+
+/// Processor that associates [SourceLocation]s from [SourceInformation] on
+/// [js.Node]s with the target offsets in a [SourceMapper].
+class PositionSourceInformationProcessor
+ extends js.BaseVisitor implements SourceInformationProcessor {
+ final CodePositionRecorder codePositions = new CodePositionRecorder();
+ final SourceMapper sourceMapper;
+
+ PositionSourceInformationProcessor(this.sourceMapper);
+
+ void process(js.Node node) {
+ node.accept(this);
+ }
+
+ void visitChildren(js.Node node) {
+ node.visitChildren(this);
+ }
+
+ CodePosition getCodePosition(js.Node node) {
+ return codePositions[node];
+ }
+
+ /// Associates [sourceInformation] with the JavaScript [node].
+ ///
+ /// The offset into the JavaScript code is computed by pulling the
+ /// [codePositionKind] from the code positions associated with
+ /// [codePositionNode].
+ ///
+ /// The mapped Dart source location is computed by pulling the
+ /// [sourcePositionKind] source location from [sourceInformation].
+ void apply(js.Node node,
+ js.Node codePositionNode,
+ CodePositionKind codePositionKind,
+ SourceInformation sourceInformation,
+ SourcePositionKind sourcePositionKind) {
+ if (sourceInformation != null) {
+ CodePosition codePosition = getCodePosition(codePositionNode);
+ // We should always have recorded the needed code positions.
+ assert(invariant(
+ NO_LOCATION_SPANNABLE,
+ codePosition != null,
+ message:
+ "Code position missing for "
+ "${nodeToString(codePositionNode)}:\n"
+ "${DebugPrinter.prettyPrint(node)}"));
+ if (codePosition == null) return;
+ int codeLocation;
+ SourceLocation sourceLocation;
+ switch (codePositionKind) {
+ case CodePositionKind.START:
+ codeLocation = codePosition.startPosition;
+ break;
+ case CodePositionKind.CLOSING:
+ codeLocation = codePosition.closingPosition;
+ break;
+ case CodePositionKind.END:
+ codeLocation = codePosition.endPosition;
+ break;
+ }
+ switch (sourcePositionKind) {
+ case SourcePositionKind.START:
+ sourceLocation = sourceInformation.startPosition;
+ break;
+ case SourcePositionKind.CLOSING:
+ sourceLocation = sourceInformation.closingPosition;
+ break;
+ case SourcePositionKind.END:
+ sourceLocation = sourceInformation.endPosition;
+ break;
+ }
+ if (codeLocation != null && sourceLocation != null) {
+ sourceMapper.register(node, codeLocation, sourceLocation);
+ }
+ }
+ }
+
+ @override
+ visitNode(js.Node node) {
+ SourceInformation sourceInformation = node.sourceInformation;
+ if (sourceInformation != null) {
+ /// Associates the left-most position of the JS code with the left-most
+ /// position of the Dart code.
+ apply(node,
+ node, CodePositionKind.START,
+ sourceInformation, SourcePositionKind.START);
+ }
+ visitChildren(node);
+ }
+
+ @override
+ visitFun(js.Fun node) {
+ SourceInformation sourceInformation = node.sourceInformation;
+ if (sourceInformation != null) {
+ /// Associates the end brace of the JavaScript function with the end brace
+ /// of the Dart function (or the `;` in case of arrow notation).
+ apply(node,
+ node, CodePositionKind.CLOSING,
+ sourceInformation, SourcePositionKind.CLOSING);
+ }
+
+ visitChildren(node);
+ }
+
+ @override
+ visitExpressionStatement(js.ExpressionStatement node) {
+ visitChildren(node);
+ }
+
+ @override
+ visitBinary(js.Binary node) {
+ visitChildren(node);
+ }
+
+ @override
+ visitAccess(js.PropertyAccess node) {
+ visitChildren(node);
+ }
+
+ @override
+ visitCall(js.Call node) {
+ SourceInformation sourceInformation = node.sourceInformation;
+ if (sourceInformation != null) {
+ if (node.target is js.PropertyAccess) {
+ js.PropertyAccess access = node.target;
+ js.Node target = access;
+ bool pureAccess = false;
+ while (target is js.PropertyAccess) {
+ js.PropertyAccess targetAccess = target;
+ if (targetAccess.receiver is js.VariableUse ||
+ targetAccess.receiver is js.This) {
+ pureAccess = true;
+ break;
+ } else {
+ target = targetAccess.receiver;
+ }
+ }
+ if (pureAccess) {
+ // a.m() this.m() a.b.c.d.m()
+ // ^ ^ ^
+ apply(
+ node,
+ node,
+ CodePositionKind.START,
+ sourceInformation,
+ SourcePositionKind.START);
+ } else {
+ // *.m() *.a.b.c.d.m()
+ // ^ ^
+ apply(
+ node,
+ access.selector,
+ CodePositionKind.START,
+ sourceInformation,
+ SourcePositionKind.CLOSING);
+ }
+ } else if (node.target is js.VariableUse) {
+ // m()
+ // ^
+ apply(
+ node,
+ node,
+ CodePositionKind.START,
+ sourceInformation,
+ SourcePositionKind.START);
+ } else if (node.target is js.Fun || node.target is js.New) {
+ // function(){}() new Function("...")()
+ // ^ ^
+ apply(
+ node,
+ node.target,
+ CodePositionKind.END,
+ sourceInformation,
+ SourcePositionKind.CLOSING);
+ } else {
+ assert(invariant(NO_LOCATION_SPANNABLE, false,
+ message: "Unexpected property access ${nodeToString(node)}:\n"
+ "${DebugPrinter.prettyPrint(node)}"));
+ // Don't know....
+ apply(
+ node,
+ node,
+ CodePositionKind.START,
+ sourceInformation,
+ SourcePositionKind.START);
+ }
+ }
+ visitChildren(node);
+ }
+
+ @override
+ void onPositions(js.Node node,
+ int startPosition,
+ int endPosition,
+ int closingPosition) {
+ codePositions.registerPositions(
+ node, startPosition, endPosition, closingPosition);
+ }
+}
diff --git a/pkg/compiler/lib/src/io/source_file.dart b/pkg/compiler/lib/src/io/source_file.dart
index 8fd28ff..d799a9b 100644
--- a/pkg/compiler/lib/src/io/source_file.dart
+++ b/pkg/compiler/lib/src/io/source_file.dart
@@ -115,6 +115,9 @@
return position - lineStarts[line];
}
+ /// Returns the offset for 0-based [line] and [column] numbers.
+ int getOffset(int line, int column) => lineStarts[line] + column;
+
String slowSubstring(int start, int end);
/**
@@ -145,13 +148,7 @@
buf.write('\n$message\n');
if (start != end && includeSourceLine) {
- String textLine;
- // +1 for 0-indexing, +1 again to avoid the last line of the file
- if ((line + 2) < lineStarts.length) {
- textLine = slowSubstring(lineStarts[line], lineStarts[line+1]);
- } else {
- textLine = '${slowSubstring(lineStarts[line], length)}\n';
- }
+ String textLine = getLineText(line);
int toColumn = min(column + (end-start), textLine.length);
buf.write(textLine.substring(0, column));
@@ -170,6 +167,20 @@
return buf.toString();
}
+
+ int get lines => lineStarts.length - 1;
+
+ /// Returns the text of line at the 0-based [index] within this source file.
+ String getLineText(int index) {
+ // +1 for 0-indexing, +1 again to avoid the last line of the file
+ if ((index + 2) < lineStarts.length) {
+ return slowSubstring(lineStarts[index], lineStarts[index+1]);
+ } else if ((index + 1) < lineStarts.length) {
+ return '${slowSubstring(lineStarts[index], length)}\n';
+ } else {
+ throw new ArgumentError("Line index $index is out of bounds.");
+ }
+ }
}
List<int> _zeroTerminateIfNecessary(List<int> bytes) {
@@ -212,7 +223,7 @@
int get length {
if (lengthCache == -1) {
// During scanning the length is not yet assigned, so we use a slow path.
- length = slowText().length;
+ lengthCache = slowText().length;
}
return lengthCache;
}
diff --git a/pkg/compiler/lib/src/io/source_information.dart b/pkg/compiler/lib/src/io/source_information.dart
index e7b23ea..3c4ea20 100644
--- a/pkg/compiler/lib/src/io/source_information.dart
+++ b/pkg/compiler/lib/src/io/source_information.dart
@@ -8,11 +8,14 @@
import '../elements/elements.dart' show
AstElement,
LocalElement;
-import '../scanner/scannerlib.dart' show Token;
-import '../tree/tree.dart' show Node;
-import '../js/js.dart' show JavaScriptNodeSourceInformation;
+import '../tree/tree.dart' show Node, Send;
+import '../js/js.dart' show
+ JavaScriptNodeSourceInformation;
import 'source_file.dart';
+bool useNewSourceInfo =
+ const bool.fromEnvironment('USE_NEW_SOURCE_INFO', defaultValue: false);
+
/// Interface for passing source information, for instance for use in source
/// maps, through the backend.
abstract class SourceInformation extends JavaScriptNodeSourceInformation {
@@ -26,14 +29,20 @@
/// The source location associated with the end of the JS node.
SourceLocation get endPosition => null;
+
+ /// All source locations associated with this source information.
+ List<SourceLocation> get sourceLocations;
+
+ /// Return a short textual representation of the source location.
+ String get shortText;
}
-/// Factory for creating [SourceInformationBuilder]s.
-class SourceInformationFactory {
- const SourceInformationFactory();
+/// Strategy for creating, processing and applying [SourceInformation].
+class SourceInformationStrategy {
+ const SourceInformationStrategy();
/// Create a [SourceInformationBuilder] for [element].
- SourceInformationBuilder forContext(AstElement element) {
+ SourceInformationBuilder createBuilderForContext(AstElement element) {
return const SourceInformationBuilder();
}
}
@@ -43,9 +52,7 @@
const SourceInformationBuilder();
/// Create a [SourceInformationBuilder] for [element].
- SourceInformationBuilder forContext(AstElement element) {
- return this;
- }
+ SourceInformationBuilder forContext(AstElement element) => this;
/// Generate [SourceInformation] the declaration of [element].
SourceInformation buildDeclaration(AstElement element) => null;
@@ -57,210 +64,32 @@
/// Generate [SourceInformation] for the return [node].
SourceInformation buildReturn(Node node) => null;
+ /// Generate [SourceInformation] for an implicit return in [element].
+ SourceInformation buildImplicitReturn(AstElement element) => null;
+
/// Generate [SourceInformation] for the loop [node].
SourceInformation buildLoop(Node node) => null;
/// Generate [SourceInformation] for the read access in [node].
SourceInformation buildGet(Node node) => null;
- /// Generate [SourceInformation] for the invocation in [node].
- SourceInformation buildCall(Node node) => null;
-}
+ /// Generate [SourceInformation] for an invocation like `a.b()` where
+ /// [receiver] points to the left-most part of the invocation, `a` in the
+ /// example, and [call] points the 'name' of the call, `b` or `()` depending
+ /// on whether `b` is a method or a field/getter.
+ SourceInformation buildCall(Node receiver, Node call) => null;
-/// Source information that contains start source position and optionally an
-/// end source position.
-class StartEndSourceInformation extends SourceInformation {
- @override
- final SourceLocation startPosition;
+ /// Generate [SourceInformation] for the if statement in [node].
+ SourceInformation buildIf(Node node) => null;
- @override
- final SourceLocation endPosition;
+ /// Generate [SourceInformation] for the constructor invocation in [node].
+ SourceInformation buildNew(Node node) => null;
- StartEndSourceInformation(this.startPosition, [this.endPosition]);
+ /// Generate [SourceInformation] for the throw in [node].
+ SourceInformation buildThrow(Node node) => null;
- @override
- SourceSpan get sourceSpan {
- Uri uri = startPosition.sourceUri;
- int begin = startPosition.offset;
- int end = endPosition == null ? begin : endPosition.offset;
- return new SourceSpan(uri, begin, end);
- }
-
- int get hashCode {
- return 0x7FFFFFFF &
- (startPosition.hashCode * 17 + endPosition.hashCode * 19);
- }
-
- bool operator ==(other) {
- if (identical(this, other)) return true;
- if (other is! StartEndSourceInformation) return false;
- return startPosition == other.startPosition &&
- endPosition == other.endPosition;
- }
-
- // TODO(johnniwinther): Remove this method. Source information should be
- // computed based on the element by provided from statements and expressions.
- static StartEndSourceInformation computeSourceInformation(
- AstElement element) {
-
- AstElement implementation = element.implementation;
- SourceFile sourceFile = implementation.compilationUnit.script.file;
- String name = computeElementNameForSourceMaps(element);
- Node node = implementation.node;
- Token beginToken;
- Token endToken;
- if (node == null) {
- // Synthesized node. Use the enclosing element for the location.
- beginToken = endToken = element.position;
- } else {
- beginToken = node.getBeginToken();
- endToken = node.getEndToken();
- }
- // TODO(podivilov): find the right sourceFile here and remove offset
- // checks below.
- SourceLocation sourcePosition, endSourcePosition;
- if (beginToken.charOffset < sourceFile.length) {
- sourcePosition =
- new OffsetSourceLocation(sourceFile, beginToken.charOffset, name);
- }
- if (endToken.charOffset < sourceFile.length) {
- endSourcePosition =
- new OffsetSourceLocation(sourceFile, endToken.charOffset, name);
- }
- return new StartEndSourceInformation(sourcePosition, endSourcePosition);
- }
-
- String toString() {
- StringBuffer sb = new StringBuffer();
- sb.write('${startPosition.sourceUri}:');
- // Use 1-based line/column info to match usual dart tool output.
- sb.write('[${startPosition.line + 1},${startPosition.column + 1}]');
- if (endPosition != null) {
- sb.write('-[${endPosition.line + 1},${endPosition.column + 1}]');
- }
- return sb.toString();
- }
-}
-
-class StartEndSourceInformationFactory implements SourceInformationFactory {
- const StartEndSourceInformationFactory();
-
- @override
- SourceInformationBuilder forContext(AstElement element) {
- return new StartEndSourceInformationBuilder(element);
- }
-}
-
-/// [SourceInformationBuilder] that generates [PositionSourceInformation].
-class StartEndSourceInformationBuilder extends SourceInformationBuilder {
- final SourceFile sourceFile;
- final String name;
-
- StartEndSourceInformationBuilder(AstElement element)
- : sourceFile = element.compilationUnit.script.file,
- name = computeElementNameForSourceMaps(element);
-
- SourceInformation buildDeclaration(AstElement element) {
- return StartEndSourceInformation.computeSourceInformation(element);
- }
-
- SourceLocation sourceFileLocationForToken(Token token) {
- SourceLocation location =
- new OffsetSourceLocation(sourceFile, token.charOffset, name);
- checkValidSourceFileLocation(location, sourceFile, token.charOffset);
- return location;
- }
-
- void checkValidSourceFileLocation(
- SourceLocation location, SourceFile sourceFile, int offset) {
- if (!location.isValid) {
- throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message(
- {'offset': offset,
- 'fileName': sourceFile.filename,
- 'length': sourceFile.length});
- }
- }
-
- @override
- SourceInformation buildLoop(Node node) {
- return new StartEndSourceInformation(
- sourceFileLocationForToken(node.getBeginToken()),
- sourceFileLocationForToken(node.getEndToken()));
- }
-
- @override
- SourceInformation buildGeneric(Node node) {
- return new StartEndSourceInformation(
- sourceFileLocationForToken(node.getBeginToken()));
- }
-
- @override
- SourceInformation buildReturn(Node node) => buildGeneric(node);
-
- @override
- SourceInformation buildGet(Node node) => buildGeneric(node);
-
- @override
- SourceInformation buildCall(Node node) => buildGeneric(node);
-
- @override
- SourceInformationBuilder forContext(
- AstElement element, {SourceInformation sourceInformation}) {
- return new StartEndSourceInformationBuilder(element);
- }
-}
-
-/// [SourceInformation] that consists of an offset position into the source
-/// code.
-class PositionSourceInformation extends SourceInformation {
- @override
- final SourceLocation startPosition;
-
- @override
- final SourceLocation closingPosition;
-
- PositionSourceInformation(this.startPosition,
- [this.closingPosition]);
-
- @override
- SourceSpan get sourceSpan {
- SourceLocation location =
- startPosition != null ? startPosition : closingPosition;
- Uri uri = location.sourceUri;
- int offset = location.offset;
- return new SourceSpan(uri, offset, offset);
- }
-
- int get hashCode {
- return 0x7FFFFFFF &
- (startPosition.hashCode * 17 + closingPosition.hashCode * 19);
- }
-
- bool operator ==(other) {
- if (identical(this, other)) return true;
- if (other is! PositionSourceInformation) return false;
- return startPosition == other.startPosition &&
- closingPosition == other.closingPosition;
- }
-
- String toString() {
- StringBuffer sb = new StringBuffer();
- if (startPosition != null) {
- sb.write('${startPosition.sourceUri}:');
- } else {
- sb.write('${closingPosition.sourceUri}:');
- }
- // Use 1-based line/column info to match usual dart tool output.
- if (startPosition != null) {
- sb.write('[${startPosition.line + 1},'
- '${startPosition.column + 1}]');
- }
- if (closingPosition != null) {
- sb.write('-[${closingPosition.line + 1},'
- '${closingPosition.column + 1}]');
- }
- return sb.toString();
- }
+ /// Generate [SourceInformation] for the assignment in [node].
+ SourceInformation buildAssignment(Node node) => null;
}
/// A location in a source file.
@@ -307,6 +136,11 @@
sourceName == other.sourceName;
}
+ String get shortText {
+ // Use 1-based line/column info to match usual dart tool output.
+ return '${sourceUri.pathSegments.last}:[${line + 1},${column + 1}]';
+ }
+
String toString() {
// Use 1-based line/column info to match usual dart tool output.
return '${sourceUri}:[${line + 1},${column + 1}]';
@@ -320,68 +154,15 @@
OffsetSourceLocation(SourceFile sourceFile, this.offset, this.sourceName)
: super(sourceFile);
+ String get shortText {
+ return '${super.shortText}:$sourceName';
+ }
+
String toString() {
return '${super.toString()}:$sourceName';
}
}
-class PositionSourceInformationFactory implements SourceInformationFactory {
- const PositionSourceInformationFactory();
-
- @override
- SourceInformationBuilder forContext(AstElement element) {
- return new PositionSourceInformationBuilder(element);
- }
-}
-
-/// [SourceInformationBuilder] that generates [PositionSourceInformation].
-class PositionSourceInformationBuilder implements SourceInformationBuilder {
- final SourceFile sourceFile;
- final String name;
-
- PositionSourceInformationBuilder(AstElement element)
- : sourceFile = element.implementation.compilationUnit.script.file,
- name = computeElementNameForSourceMaps(element);
-
- SourceInformation buildDeclaration(AstElement element) {
- if (element.isSynthesized) {
- return new PositionSourceInformation(
- new OffsetSourceLocation(
- sourceFile, element.position.charOffset, name));
- } else {
- return new PositionSourceInformation(
- null,
- new OffsetSourceLocation(sourceFile,
- element.resolvedAst.node.getEndToken().charOffset, name));
- }
- }
-
- SourceInformation buildBegin(Node node) {
- return new PositionSourceInformation(new OffsetSourceLocation(
- sourceFile, node.getBeginToken().charOffset, name));
- }
-
- @override
- SourceInformation buildGeneric(Node node) => buildBegin(node);
-
- @override
- SourceInformation buildReturn(Node node) => buildBegin(node);
-
- @override
- SourceInformation buildLoop(Node node) => buildBegin(node);
-
- @override
- SourceInformation buildGet(Node node) => buildBegin(node);
-
- @override
- SourceInformation buildCall(Node node) => buildBegin(node);
-
- @override
- SourceInformationBuilder forContext(AstElement element) {
- return new PositionSourceInformationBuilder(element);
- }
-}
-
/// Compute the source map name for [element].
String computeElementNameForSourceMaps(AstElement element) {
if (element.isClosure) {
@@ -409,4 +190,4 @@
} else {
return element.name;
}
-}
\ No newline at end of file
+}
diff --git a/pkg/compiler/lib/src/io/start_end_information.dart b/pkg/compiler/lib/src/io/start_end_information.dart
new file mode 100644
index 0000000..b76a217
--- /dev/null
+++ b/pkg/compiler/lib/src/io/start_end_information.dart
@@ -0,0 +1,232 @@
+// Copyright (c) 2015, 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.
+
+/// Source information system that maps spans of Dart AST nodes to spans of
+/// JavaScript nodes.
+
+library dart2js.source_information.start_end;
+
+import '../dart2jslib.dart' show
+ MessageKind,
+ SourceSpan;
+import '../elements/elements.dart' show
+ AstElement,
+ LocalElement;
+import '../js/js.dart' as js;
+import '../js/js_source_mapping.dart';
+import '../scanner/scannerlib.dart' show Token;
+import '../tree/tree.dart' show Node, Send;
+
+import 'source_file.dart';
+import 'source_information.dart';
+
+/// Source information that contains start source position and optionally an
+/// end source position.
+class StartEndSourceInformation extends SourceInformation {
+ @override
+ final SourceLocation startPosition;
+
+ @override
+ final SourceLocation endPosition;
+
+ StartEndSourceInformation(this.startPosition, [this.endPosition]);
+
+ @override
+ List<SourceLocation> get sourceLocations {
+ if (endPosition == null) {
+ return <SourceLocation>[startPosition];
+ } else {
+ return <SourceLocation>[startPosition, endPosition];
+ }
+ }
+
+ @override
+ SourceSpan get sourceSpan {
+ Uri uri = startPosition.sourceUri;
+ int begin = startPosition.offset;
+ int end = endPosition == null ? begin : endPosition.offset;
+ return new SourceSpan(uri, begin, end);
+ }
+
+ int get hashCode {
+ return 0x7FFFFFFF &
+ (startPosition.hashCode * 17 + endPosition.hashCode * 19);
+ }
+
+ bool operator ==(other) {
+ if (identical(this, other)) return true;
+ if (other is! StartEndSourceInformation) return false;
+ return startPosition == other.startPosition &&
+ endPosition == other.endPosition;
+ }
+
+ // TODO(johnniwinther): Inline this in
+ // [StartEndSourceInformationBuilder.buildDeclaration].
+ static StartEndSourceInformation _computeSourceInformation(
+ AstElement element) {
+
+ AstElement implementation = element.implementation;
+ SourceFile sourceFile = implementation.compilationUnit.script.file;
+ String name = computeElementNameForSourceMaps(element);
+ Node node = implementation.node;
+ Token beginToken;
+ Token endToken;
+ if (node == null) {
+ // Synthesized node. Use the enclosing element for the location.
+ beginToken = endToken = element.position;
+ } else {
+ beginToken = node.getBeginToken();
+ endToken = node.getEndToken();
+ }
+ // TODO(johnniwinther): find the right sourceFile here and remove offset
+ // checks below.
+ SourceLocation sourcePosition, endSourcePosition;
+ if (beginToken.charOffset < sourceFile.length) {
+ sourcePosition =
+ new OffsetSourceLocation(sourceFile, beginToken.charOffset, name);
+ }
+ if (endToken.charOffset < sourceFile.length) {
+ endSourcePosition =
+ new OffsetSourceLocation(sourceFile, endToken.charOffset, name);
+ }
+ return new StartEndSourceInformation(sourcePosition, endSourcePosition);
+ }
+
+ /// Create a textual representation of the source information using [uriText]
+ /// as the Uri representation.
+ String _computeText(String uriText) {
+ StringBuffer sb = new StringBuffer();
+ sb.write('$uriText:');
+ // Use 1-based line/startPosition info to match usual dart tool output.
+ sb.write('[${startPosition.line + 1},${startPosition.column + 1}]');
+ if (endPosition != null) {
+ sb.write('-[${endPosition.line + 1},${endPosition.column + 1}]');
+ }
+ return sb.toString();
+ }
+
+ String get shortText {
+ return _computeText(startPosition.sourceUri.pathSegments.last);
+ }
+
+ String toString() {
+ return _computeText('${startPosition.sourceUri}');
+ }
+}
+
+class StartEndSourceInformationStrategy
+ implements JavaScriptSourceInformationStrategy {
+ const StartEndSourceInformationStrategy();
+
+ @override
+ SourceInformationBuilder createBuilderForContext(AstElement element) {
+ return new StartEndSourceInformationBuilder(element);
+ }
+
+ @override
+ SourceInformationProcessor createProcessor(SourceMapper sourceMapper) {
+ return new StartEndSourceInformationProcessor(sourceMapper);
+ }
+}
+
+class StartEndSourceInformationProcessor extends SourceInformationProcessor {
+ final SourceMapper sourceMapper;
+
+ /// Used to track whether a terminating source location marker has been
+ /// registered for the top-most node with source information.
+ bool hasRegisteredRoot = false;
+
+ StartEndSourceInformationProcessor(this.sourceMapper);
+
+ @override
+ void onPositions(js.Node node,
+ int startPosition,
+ int endPosition,
+ int closingPosition) {
+ if (node.sourceInformation != null) {
+ StartEndSourceInformation sourceInformation = node.sourceInformation;
+ sourceMapper.register(
+ node, startPosition, sourceInformation.startPosition);
+ if (sourceInformation.endPosition != null) {
+ sourceMapper.register(node, endPosition, sourceInformation.endPosition);
+ }
+ if (!hasRegisteredRoot) {
+ sourceMapper.register(node, endPosition, null);
+ hasRegisteredRoot = true;
+ }
+ }
+ }
+}
+
+/// [SourceInformationBuilder] that generates [PositionSourceInformation].
+class StartEndSourceInformationBuilder extends SourceInformationBuilder {
+ final SourceFile sourceFile;
+ final String name;
+
+ StartEndSourceInformationBuilder(AstElement element)
+ : sourceFile = element.compilationUnit.script.file,
+ name = computeElementNameForSourceMaps(element);
+
+ SourceInformation buildDeclaration(AstElement element) {
+ return StartEndSourceInformation._computeSourceInformation(element);
+ }
+
+ SourceLocation sourceFileLocationForToken(Token token) {
+ SourceLocation location =
+ new OffsetSourceLocation(sourceFile, token.charOffset, name);
+ checkValidSourceFileLocation(location, sourceFile, token.charOffset);
+ return location;
+ }
+
+ void checkValidSourceFileLocation(
+ SourceLocation location, SourceFile sourceFile, int offset) {
+ if (!location.isValid) {
+ throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message(
+ {'offset': offset,
+ 'fileName': sourceFile.filename,
+ 'length': sourceFile.length});
+ }
+ }
+
+ @override
+ SourceInformation buildLoop(Node node) {
+ return new StartEndSourceInformation(
+ sourceFileLocationForToken(node.getBeginToken()),
+ sourceFileLocationForToken(node.getEndToken()));
+ }
+
+ @override
+ SourceInformation buildGeneric(Node node) {
+ return new StartEndSourceInformation(
+ sourceFileLocationForToken(node.getBeginToken()));
+ }
+
+ @override
+ SourceInformation buildReturn(Node node) {
+ return buildGeneric(node);
+ }
+
+ @override
+ SourceInformation buildGet(Node node) => buildGeneric(node);
+
+ @override
+ SourceInformation buildAssignment(Node node) => buildGeneric(node);
+
+ @override
+ SourceInformation buildCall(Node receiver, Node call) {
+ return buildGeneric(receiver);
+ }
+
+ @override
+ SourceInformation buildIf(Node node) => buildGeneric(node);
+
+ @override
+ SourceInformationBuilder forContext(
+ AstElement element, {SourceInformation sourceInformation}) {
+ return new StartEndSourceInformationBuilder(element);
+ }
+}
+
+
+
diff --git a/pkg/compiler/lib/src/js/js.dart b/pkg/compiler/lib/src/js/js.dart
index 2d208a6..f609781 100644
--- a/pkg/compiler/lib/src/js/js.dart
+++ b/pkg/compiler/lib/src/js/js.dart
@@ -7,35 +7,50 @@
import 'package:js_ast/js_ast.dart';
export 'package:js_ast/js_ast.dart';
-import '../io/code_output.dart' show CodeBuffer;
-import '../io/source_information.dart' show SourceInformation;
-import '../js_emitter/js_emitter.dart' show USE_NEW_EMITTER;
+import '../io/code_output.dart' show CodeOutput, CodeBuffer;
+import '../js_emitter/js_emitter.dart' show USE_LAZY_EMITTER;
import '../dart2jslib.dart' as leg;
-import '../util/util.dart' show NO_LOCATION_SPANNABLE;
+import '../util/util.dart' show NO_LOCATION_SPANNABLE, Indentation, Tagging;
import '../dump_info.dart' show DumpInfoTask;
+import 'js_source_mapping.dart';
-CodeBuffer prettyPrint(Node node, leg.Compiler compiler,
+CodeBuffer prettyPrint(Node node,
+ leg.Compiler compiler,
{DumpInfoTask monitor,
- bool allowVariableMinification: true}) {
+ bool allowVariableMinification: true,
+ Renamer renamerForNames:
+ JavaScriptPrintingOptions.identityRenamer}) {
+ JavaScriptSourceInformationStrategy sourceInformationFactory =
+ compiler.backend.sourceInformationStrategy;
JavaScriptPrintingOptions options = new JavaScriptPrintingOptions(
shouldCompressOutput: compiler.enableMinification,
minifyLocalVariables: allowVariableMinification,
- preferSemicolonToNewlineInMinifiedOutput: USE_NEW_EMITTER);
+ preferSemicolonToNewlineInMinifiedOutput: USE_LAZY_EMITTER,
+ renamerForNames: renamerForNames);
+ CodeBuffer outBuffer = new CodeBuffer();
+ SourceInformationProcessor sourceInformationProcessor =
+ sourceInformationFactory.createProcessor(
+ new SourceLocationsMapper(outBuffer));
Dart2JSJavaScriptPrintingContext context =
- new Dart2JSJavaScriptPrintingContext(compiler, monitor);
+ new Dart2JSJavaScriptPrintingContext(
+ compiler, monitor, outBuffer, sourceInformationProcessor);
Printer printer = new Printer(options, context);
printer.visit(node);
- return context.outBuffer;
+ sourceInformationProcessor.process(node);
+ return outBuffer;
}
class Dart2JSJavaScriptPrintingContext implements JavaScriptPrintingContext {
final leg.Compiler compiler;
final DumpInfoTask monitor;
- final CodeBuffer outBuffer = new CodeBuffer();
- Node rootNode;
+ final CodeBuffer outBuffer;
+ final CodePositionListener codePositionListener;
- Dart2JSJavaScriptPrintingContext(leg.Compiler this.compiler,
- DumpInfoTask this.monitor);
+ Dart2JSJavaScriptPrintingContext(
+ this.compiler,
+ this.monitor,
+ this.outBuffer,
+ this.codePositionListener);
@override
void error(String message) {
@@ -48,40 +63,97 @@
}
@override
- void enterNode(Node node, int startPosition) {
- SourceInformation sourceInformation = node.sourceInformation;
- if (sourceInformation != null) {
- if (rootNode == null) {
- rootNode = node;
- }
- if (sourceInformation.startPosition != null) {
- outBuffer.addSourceLocation(
- startPosition, sourceInformation.startPosition);
- }
- }
- }
+ void enterNode(Node, int startPosition) {}
+ @override
void exitNode(Node node,
int startPosition,
int endPosition,
int closingPosition) {
- SourceInformation sourceInformation = node.sourceInformation;
- if (sourceInformation != null) {
- if (closingPosition != null &&
- sourceInformation.closingPosition != null) {
- outBuffer.addSourceLocation(
- closingPosition, sourceInformation.closingPosition);
- }
- if (sourceInformation.endPosition != null) {
- outBuffer.addSourceLocation(endPosition, sourceInformation.endPosition);
- }
- if (rootNode == node) {
- outBuffer.addSourceLocation(endPosition, null);
- rootNode = null;
- }
- }
if (monitor != null) {
monitor.recordAstSize(node, endPosition - startPosition);
}
+ codePositionListener.onPositions(
+ node, startPosition, endPosition, closingPosition);
}
}
+
+/// Interface for ast nodes that encapsulate an ast that needs to be
+/// traversed when counting tokens.
+abstract class AstContainer implements Node {
+ Iterable<Node> get containedNodes;
+}
+
+/// Interface for tasks in the compiler that need to finalize tokens after
+/// counting them.
+abstract class TokenFinalizer {
+ void finalizeTokens();
+}
+
+/// Implements reference counting for instances of [ReferenceCountedAstNode]
+class TokenCounter extends BaseVisitor {
+ @override
+ visitNode(Node node) {
+ if (node is AstContainer) {
+ for (Node element in node.containedNodes) {
+ element.accept(this);
+ }
+ } else if (node is ReferenceCountedAstNode) {
+ node.markSeen(this);
+ } else {
+ super.visitNode(node);
+ }
+ }
+
+ void countTokens(Node node) => node.accept(this);
+}
+
+abstract class ReferenceCountedAstNode implements Node {
+ markSeen(TokenCounter visitor);
+}
+
+/// Represents the LiteralString resulting from unparsing [expression]. The
+/// actual unparsing is done on demand when requesting the [value] of this
+/// node.
+///
+/// This is used when generated code needs to be represented as a string,
+/// for example by the lazy emitter or when generating code generators.
+class UnparsedNode extends DeferredString
+ implements AstContainer {
+ @override
+ final Node tree;
+ final leg.Compiler _compiler;
+ final bool _protectForEval;
+ LiteralString _cachedLiteral;
+
+ Iterable<Node> get containedNodes => [tree];
+
+ /// A [js.Literal] that represents the string result of unparsing [ast].
+ ///
+ /// When its string [value] is requested, the node pretty-prints the given
+ /// [ast] and, if [protectForEval] is true, wraps the resulting
+ /// string in parenthesis. The result is also escaped.
+ UnparsedNode(this.tree, this._compiler, this._protectForEval);
+
+ LiteralString get _literal {
+ if (_cachedLiteral == null) {
+ String text = prettyPrint(tree, _compiler).getText();
+ if (_protectForEval) {
+ if (tree is Fun) text = '($text)';
+ if (tree is LiteralExpression) {
+ LiteralExpression literalExpression = tree;
+ String template = literalExpression.template;
+ if (template.startsWith("function ") ||
+ template.startsWith("{")) {
+ text = '($text)';
+ }
+ }
+ }
+ _cachedLiteral = js.escapedString(text);
+ }
+ return _cachedLiteral;
+ }
+
+ @override
+ String get value => _literal.value;
+}
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/js/js_debug.dart b/pkg/compiler/lib/src/js/js_debug.dart
new file mode 100644
index 0000000..43ec130
--- /dev/null
+++ b/pkg/compiler/lib/src/js/js_debug.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2015, 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.
+
+/// Helper for debug JS nodes.
+
+library js.debug;
+
+import 'package:js_ast/js_ast.dart';
+import '../util/util.dart' show Indentation, Tagging;
+
+/// Unparse the JavaScript [node].
+String nodeToString(Node node) {
+ JavaScriptPrintingOptions options = new JavaScriptPrintingOptions(
+ shouldCompressOutput: true,
+ preferSemicolonToNewlineInMinifiedOutput: true);
+ LenientPrintingContext printingContext =
+ new LenientPrintingContext();
+ new Printer(options, printingContext).visit(node);
+ return printingContext.getText();
+}
+
+/// Visitor that creates an XML-like representation of the structure of a
+/// JavaScript [Node].
+class DebugPrinter extends BaseVisitor with Indentation, Tagging<Node> {
+ StringBuffer sb = new StringBuffer();
+
+ void visitNodeWithChildren(Node node, String type) {
+ openNode(node, type);
+ node.visitChildren(this);
+ closeNode();
+ }
+
+ @override
+ void visitNode(Node node) {
+ visitNodeWithChildren(node, '${node.runtimeType}');
+ }
+
+ @override
+ void visitName(Name node) {
+ openAndCloseNode(node, '${node.runtimeType}', {'name': node.name});
+ }
+
+ @override
+ void visitLiteralString(LiteralString node) {
+ openAndCloseNode(node, '${node.runtimeType}', {'value': node.value});
+ }
+
+ /**
+ * Pretty-prints given node tree into string.
+ */
+ static String prettyPrint(Node node) {
+ var p = new DebugPrinter();
+ node.accept(p);
+ return p.sb.toString();
+ }
+}
+
+/// Simple printing context that doesn't throw on errors.
+class LenientPrintingContext extends SimpleJavaScriptPrintingContext {
+ @override
+ void error(String message) {
+ buffer.write('>>$message<<');
+ }
+}
diff --git a/pkg/compiler/lib/src/js/js_source_mapping.dart b/pkg/compiler/lib/src/js/js_source_mapping.dart
new file mode 100644
index 0000000..0378a58
--- /dev/null
+++ b/pkg/compiler/lib/src/js/js_source_mapping.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2015, 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.
+
+library js.source_mapping;
+
+import 'js.dart';
+import '../io/code_output.dart' show SourceLocations;
+import '../io/source_information.dart' show
+ SourceLocation,
+ SourceInformation,
+ SourceInformationStrategy;
+
+/// [SourceInformationStrategy] that can associate source information with
+/// JavaScript output.
+class JavaScriptSourceInformationStrategy
+ extends SourceInformationStrategy {
+ const JavaScriptSourceInformationStrategy();
+
+ /// Creates a processor that can associate source information on [Node] with
+ /// code offsets in the [sourceMapper].
+ SourceInformationProcessor createProcessor(SourceMapper sourceMapper) {
+ return const SourceInformationProcessor();
+ }
+}
+
+/// An observer of code positions of printed JavaScript [Node]s.
+class CodePositionListener {
+ const CodePositionListener();
+
+ /// Called to associate [node] with the provided start, end and closing
+ /// positions.
+ void onPositions(
+ Node node,
+ int startPosition,
+ int endPosition,
+ int closingPosition) {}
+}
+
+/// An interface for mapping code offsets with [SourceLocation]s for JavaScript
+/// [Node]s.
+abstract class SourceMapper {
+ /// Associate [codeOffset] with [sourceLocation] for [node].
+ void register(Node node, int codeOffset, SourceLocation sourceLocation);
+}
+
+/// An implementation of [SourceMapper] that stores the information directly
+/// into a [SourceLocations] object.
+class SourceLocationsMapper implements SourceMapper {
+ final SourceLocations sourceLocations;
+
+ SourceLocationsMapper(this.sourceLocations);
+
+ @override
+ void register(Node node, int codeOffset, SourceLocation sourceLocation) {
+ sourceLocations.addSourceLocation(codeOffset, sourceLocation);
+ }
+}
+
+/// A processor that associates [SourceInformation] with code position of
+/// JavaScript [Node]s.
+class SourceInformationProcessor extends CodePositionListener {
+ const SourceInformationProcessor();
+
+ /// Process the source information and code positions for the [node] and all
+ /// its children.
+ void process(Node node) {}
+}
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index ddeacb4..c48fed1 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -242,7 +242,7 @@
static const String START_ROOT_ISOLATE = 'startRootIsolate';
- String get patchVersion => USE_NEW_EMITTER ? 'new' : 'old';
+ String get patchVersion => USE_LAZY_EMITTER ? 'lazy' : 'full';
final Annotations annotations;
@@ -617,8 +617,9 @@
bool enabledNoSuchMethod = false;
+ final SourceInformationStrategy sourceInformationStrategy;
+
JavaScriptBackend(Compiler compiler,
- SourceInformationFactory sourceInformationFactory,
{bool generateSourceMap: true})
: namer = determineNamer(compiler),
oneShotInterceptors = new Map<jsAst.Name, Selector>(),
@@ -626,6 +627,12 @@
rti = new RuntimeTypes(compiler),
specializedGetInterceptors = new Map<jsAst.Name, Set<ClassElement>>(),
annotations = new Annotations(compiler),
+ this.sourceInformationStrategy =
+ generateSourceMap
+ ? (useNewSourceInfo
+ ? const PositionSourceInformationStrategy()
+ : const StartEndSourceInformationStrategy())
+ : const JavaScriptSourceInformationStrategy(),
super(compiler) {
emitter = new CodeEmitterTask(compiler, namer, generateSourceMap);
typeVariableHandler = new TypeVariableHandler(compiler);
@@ -636,8 +643,8 @@
patchResolverTask = new PatchResolverTask(compiler);
functionCompiler = compiler.useCpsIr
? new CpsFunctionCompiler(
- compiler, this, sourceInformationFactory)
- : new SsaFunctionCompiler(this, sourceInformationFactory);
+ compiler, this, sourceInformationStrategy)
+ : new SsaFunctionCompiler(this, sourceInformationStrategy);
}
ConstantSystem get constantSystem => constants.constantSystem;
@@ -676,7 +683,9 @@
static Namer determineNamer(Compiler compiler) {
return compiler.enableMinification ?
- new MinifyNamer(compiler) :
+ compiler.useFrequencyNamer ?
+ new FrequencyBasedNamer(compiler) :
+ new MinifyNamer(compiler) :
new Namer(compiler);
}
@@ -1462,6 +1471,14 @@
*/
String assembleCode(Element element) {
assert(invariant(element, element.isDeclaration));
+ var code = generatedCode[element];
+ if (namer is jsAst.TokenFinalizer) {
+ jsAst.TokenCounter counter = new jsAst.TokenCounter();
+ counter.countTokens(code);
+ // Avoid a warning.
+ var finalizer = namer;
+ finalizer.finalizeTokens();
+ }
return jsAst.prettyPrint(generatedCode[element], compiler).getText();
}
diff --git a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart
index cbead55..1447173 100644
--- a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart
@@ -191,28 +191,13 @@
return buildConstant(glue.getConstantValueForVariable(parameter));
}
- // TODO(karlklose): get rid of the selector argument.
- js.Expression buildStaticInvoke(Selector selector,
- Element target,
+ js.Expression buildStaticInvoke(Element target,
List<js.Expression> arguments,
{SourceInformation sourceInformation}) {
registry.registerStaticInvocation(target.declaration);
- if (target == glue.getInterceptorMethod) {
- // This generates a call to the specialized interceptor function, which
- // does not have a specialized element yet, but is emitted as a stub from
- // the emitter in [InterceptorStubGenerator].
- // TODO(karlklose): Either change [InvokeStatic] to take an [Entity]
- // instead of an [Element] and model the getInterceptor functions as
- // [Entity]s or add a specialized Tree-IR node for interceptor calls.
- registry.registerUseInterceptor();
- js.VariableUse interceptorLibrary = glue.getInterceptorLibrary();
- return js.propertyCall(interceptorLibrary, js.string(selector.name),
- arguments);
- } else {
- js.Expression elementAccess = glue.staticFunctionAccess(target);
- return new js.Call(elementAccess, arguments,
- sourceInformation: sourceInformation);
- }
+ js.Expression elementAccess = glue.staticFunctionAccess(target);
+ return new js.Call(elementAccess, arguments,
+ sourceInformation: sourceInformation);
}
@override
@@ -220,10 +205,9 @@
if (node.constant != null) return giveup(node);
registry.registerInstantiatedType(node.type);
- Selector selector = node.selector;
FunctionElement target = node.target;
List<js.Expression> arguments = visitExpressionList(node.arguments);
- return buildStaticInvoke(selector, target, arguments);
+ return buildStaticInvoke(target, arguments);
}
void registerMethodInvoke(tree_ir.InvokeMethod node) {
@@ -255,12 +239,10 @@
@override
js.Expression visitInvokeStatic(tree_ir.InvokeStatic node) {
- Selector selector = node.selector;
- assert(selector.isGetter || selector.isSetter || selector.isCall);
FunctionElement target = node.target;
List<js.Expression> arguments = visitExpressionList(node.arguments);
- return buildStaticInvoke(selector, target, arguments,
- sourceInformation: node.sourceInformation);
+ return buildStaticInvoke(target, arguments,
+ sourceInformation: node.sourceInformation);
}
@override
@@ -305,10 +287,7 @@
List<js.Expression> args = entries.isEmpty
? <js.Expression>[]
: <js.Expression>[new js.ArrayInitializer(entries)];
- return buildStaticInvoke(
- new Selector.call(constructor.name, constructor.library, 2),
- constructor,
- args);
+ return buildStaticInvoke(constructor, args);
}
@override
@@ -346,6 +325,11 @@
return js.js(r'!#.immutable$list', <js.Expression>[value]);
}
+ // The helper we use needs the JSArray class to exist, but for some
+ // reason the helper does not cause this dependency to be registered.
+ // TODO(asgerf): Most programs need List anyway, but we should fix this.
+ registry.registerInstantiatedClass(glue.listClass);
+
// We use one of the two helpers:
//
// checkSubtype(value, $isT, typeArgs, $asT)
@@ -683,8 +667,7 @@
js.Expression buildStaticHelperInvocation(FunctionElement helper,
List<js.Expression> arguments) {
registry.registerStaticUse(helper);
- return buildStaticInvoke(new Selector.fromElement(helper), helper,
- arguments);
+ return buildStaticInvoke(helper, arguments);
}
@override
diff --git a/pkg/compiler/lib/src/js_backend/codegen/task.dart b/pkg/compiler/lib/src/js_backend/codegen/task.dart
index 25492d6..9ed2829 100644
--- a/pkg/compiler/lib/src/js_backend/codegen/task.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen/task.dart
@@ -20,7 +20,7 @@
ForwardingTypeMask;
import '../../elements/elements.dart';
import '../../js/js.dart' as js;
-import '../../io/source_information.dart' show SourceInformationFactory;
+import '../../io/source_information.dart' show SourceInformationStrategy;
import '../../tree_ir/tree_ir_builder.dart' as tree_builder;
import '../../cps_ir/optimizers.dart';
import '../../cps_ir/optimizers.dart' as cps_opt;
@@ -36,7 +36,7 @@
final ConstantSystem constantSystem;
final Compiler compiler;
final Glue glue;
- final SourceInformationFactory sourceInformationFactory;
+ final SourceInformationStrategy sourceInformationFactory;
// TODO(karlklose,sigurm): remove and update dart-doc of [compile].
final FunctionCompiler fallbackCompiler;
@@ -46,7 +46,7 @@
IrBuilderTask get irBuilderTask => compiler.irBuilder;
CpsFunctionCompiler(Compiler compiler, JavaScriptBackend backend,
- SourceInformationFactory sourceInformationFactory)
+ SourceInformationStrategy sourceInformationFactory)
: fallbackCompiler =
new ssa.SsaFunctionCompiler(backend, sourceInformationFactory),
this.sourceInformationFactory = sourceInformationFactory,
@@ -234,7 +234,7 @@
js.Node attachPosition(js.Node node, AstElement element) {
return node.withSourceInformation(
- sourceInformationFactory.forContext(element)
+ sourceInformationFactory.createBuilderForContext(element)
.buildDeclaration(element));
}
}
diff --git a/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart
index 1666ca5..707e7e76 100644
--- a/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart
+++ b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart
@@ -44,7 +44,7 @@
* hierarchy is encoded using instances of [_FieldNamingScope].
*/
class _FieldNamingRegistry {
- final MinifyNamer namer;
+ final Namer namer;
final Map<Entity, _FieldNamingScope> scopes =
new Map<Entity, _FieldNamingScope>();
@@ -77,8 +77,7 @@
nameStore.add(
new StringBackedName(MinifyNamer._reservedNativeProperties[index]));
} else {
- nameStore.add(namer.getFreshName("field$index", namer.usedInstanceNames,
- namer.suggestedInstanceNames));
+ nameStore.add(namer.getFreshName(NamingScope.instance, "field$index"));
}
}
diff --git a/pkg/compiler/lib/src/js_backend/frequency_namer.dart b/pkg/compiler/lib/src/js_backend/frequency_namer.dart
new file mode 100644
index 0000000..0aaf8f6
--- /dev/null
+++ b/pkg/compiler/lib/src/js_backend/frequency_namer.dart
@@ -0,0 +1,142 @@
+// Copyright (c) 2015, 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.
+
+part of js_backend;
+
+class FrequencyBasedNamer extends Namer with _MinifiedFieldNamer,
+ _MinifiedOneShotInterceptorNamer implements jsAst.TokenFinalizer {
+ _FieldNamingRegistry fieldRegistry;
+ List<TokenName> tokens = new List<TokenName>();
+
+ Map<NamingScope, TokenScope> _tokenScopes =
+ new Maplet<NamingScope, TokenScope>();
+
+ // Some basic settings for smaller names
+ String get isolateName => 'I';
+ String get isolatePropertiesName => 'p';
+ bool get shouldMinify => true;
+
+ final String getterPrefix = 'g';
+ final String setterPrefix = 's';
+ final String callPrefix = ''; // this will create function names $<n>
+
+ FrequencyBasedNamer(Compiler compiler) : super(compiler) {
+ fieldRegistry = new _FieldNamingRegistry(this);
+ }
+
+ TokenScope newScopeFor(NamingScope scope) {
+ if (scope == NamingScope.instance) {
+ Set<String> illegalNames = new Set<String>.from(jsReserved);
+ for (String illegal in MinifyNamer._reservedNativeProperties) {
+ illegalNames.add(illegal);
+ if (MinifyNamer._hasBannedPrefix(illegal)) {
+ illegalNames.add(illegal.substring(1));
+ }
+ }
+ return new TokenScope(illegalNames);
+ } else {
+ return new TokenScope(jsReserved);
+ }
+ }
+
+ @override
+ jsAst.Name getFreshName(NamingScope scope, String proposedName,
+ {bool sanitizeForNatives: false,
+ bool sanitizeForAnnotations: false}) {
+ // Grab the scope for this token
+ TokenScope tokenScope = _tokenScopes.putIfAbsent(scope,
+ () => newScopeFor(scope));
+
+ // Get the name the normal namer would use as a key.
+ String proposed = _generateFreshStringForName(proposedName,
+ getUsedNames(scope),
+ getSuggestedNames(scope),
+ sanitizeForNatives:
+ sanitizeForNatives,
+ sanitizeForAnnotations:
+ sanitizeForAnnotations);
+
+ TokenName name = new TokenName(tokenScope, proposed);
+ tokens.add(name);
+ return name;
+ }
+
+ @override
+ jsAst.Name instanceFieldPropertyName(Element element) {
+ jsAst.Name proposed = _minifiedInstanceFieldPropertyName(element);
+ if (proposed != null) {
+ return proposed;
+ }
+ return super.instanceFieldPropertyName(element);
+ }
+
+ @override
+ void finalizeTokens() {
+ int compareReferenceCount(TokenName a, TokenName b) {
+ int result = b._rc - a._rc;
+ if (result == 0) result = a.key.compareTo(b.key);
+ return result;
+ }
+
+ List<TokenName> usedNames =
+ tokens.where((TokenName a) => a._rc > 0).toList();
+ usedNames.sort(compareReferenceCount);
+ usedNames.forEach((TokenName token) => token.finalize());
+ }
+}
+
+class TokenScope {
+ List<int> _nextName = [$a];
+ final Set<String> illegalNames;
+
+ TokenScope([this.illegalNames = const ImmutableEmptySet()]);
+
+ /// Increments the letter at [pos] in the current name. Also takes care of
+ /// overflows to the left. Returns the carry bit, i.e., it returns `true`
+ /// if all positions to the left have wrapped around.
+ ///
+ /// If [_nextName] is initially 'a', this will generate the sequence
+ ///
+ /// [a-zA-Z]
+ /// [a-zA-Z][_0-9a-zA-Z]
+ /// [a-zA-Z][_0-9a-zA-Z][_0-9a-zA-Z]
+ /// ...
+ bool _incrementPosition(int pos) {
+ bool overflow = false;
+ if (pos < 0) return true;
+ int value = _nextName[pos];
+ if (value == $_) {
+ value = $0;
+ } else if (value == $9) {
+ value = $a;
+ } else if (value == $z) {
+ value = $A;
+ } else if (value == $Z) {
+ overflow = _incrementPosition(pos - 1);
+ value = (pos > 0) ? $_ : $a;
+ } else {
+ value++;
+ }
+ _nextName[pos] = value;
+ return overflow;
+ }
+
+ _incrementName() {
+ if (_incrementPosition(_nextName.length - 1)) {
+ _nextName.add($_);
+ }
+ }
+
+ String getNextName() {
+ String proposal;
+ do {
+ proposal = new String.fromCharCodes(_nextName);
+ _incrementName();
+ } while (MinifyNamer._hasBannedPrefix(proposal) ||
+ illegalNames.contains(proposal));
+
+ return proposal;
+ }
+}
+
diff --git a/pkg/compiler/lib/src/js_backend/js_backend.dart b/pkg/compiler/lib/src/js_backend/js_backend.dart
index 36e75ab..8608f00 100644
--- a/pkg/compiler/lib/src/js_backend/js_backend.dart
+++ b/pkg/compiler/lib/src/js_backend/js_backend.dart
@@ -19,16 +19,20 @@
import '../dart_types.dart';
import '../elements/elements.dart';
import '../io/code_output.dart';
-import '../io/source_information.dart' show SourceInformationFactory;
+import '../io/source_information.dart' show
+ SourceInformationStrategy,
+ useNewSourceInfo;
+import '../io/position_information.dart' show
+ PositionSourceInformationStrategy;
+import '../io/start_end_information.dart' show
+ StartEndSourceInformationStrategy;
import '../js/js.dart' as jsAst;
import '../js/js.dart' show js;
+import '../js/js_source_mapping.dart' show
+ JavaScriptSourceInformationStrategy;
import '../js_emitter/js_emitter.dart'
- show Emitter,
- CodeEmitterTask,
- ClassBuilder,
- MetadataCollector,
- Placeholder,
- USE_NEW_EMITTER;
+ show ClassBuilder, CodeEmitterTask, Emitter, MetadataCollector, Placeholder,
+ TokenFinalizer, USE_LAZY_EMITTER;
import '../library_loader.dart' show LibraryLoader, LoadedLibraries;
import '../native/native.dart' as native;
@@ -53,9 +57,11 @@
part 'constant_emitter.dart';
part 'constant_handler_javascript.dart';
part 'custom_elements_analysis.dart';
+part 'frequency_namer.dart';
part 'field_naming_mixin.dart';
part 'minify_namer.dart';
part 'namer.dart';
+part 'namer_names.dart';
part 'no_such_method_registry.dart';
part 'runtime_types.dart';
part 'type_variable_handler.dart';
diff --git a/pkg/compiler/lib/src/js_backend/minify_namer.dart b/pkg/compiler/lib/src/js_backend/minify_namer.dart
index 9a2d1e3..dd01a92 100644
--- a/pkg/compiler/lib/src/js_backend/minify_namer.dart
+++ b/pkg/compiler/lib/src/js_backend/minify_namer.dart
@@ -7,7 +7,8 @@
/**
* Assigns JavaScript identifiers to Dart variables, class-names and members.
*/
-class MinifyNamer extends Namer with _MinifiedFieldNamer {
+class MinifyNamer extends Namer with _MinifiedFieldNamer,
+ _MinifyConstructorBodyNamer, _MinifiedOneShotInterceptorNamer {
MinifyNamer(Compiler compiler) : super(compiler) {
reserveBackendNames();
fieldRegistry = new _FieldNamingRegistry(this);
@@ -32,11 +33,11 @@
/// [sanitizeForNatives] and [sanitizeForAnnotations] are ignored because the
/// minified names will always avoid clashing with annotated names or natives.
@override
- jsAst.Name getFreshName(String proposedName,
- Set<String> usedNames,
- Map<String, String> suggestedNames,
- {bool sanitizeForNatives: false,
- bool sanitizeForAnnotations: false}) {
+ String _generateFreshStringForName(String proposedName,
+ Set<String> usedNames,
+ Map<String, String> suggestedNames,
+ {bool sanitizeForNatives: false,
+ bool sanitizeForAnnotations: false}) {
String freshName;
String suggestion = suggestedNames[proposedName];
if (suggestion != null && !usedNames.contains(suggestion)) {
@@ -46,7 +47,7 @@
suggestedNames.values);
}
usedNames.add(freshName);
- return new StringBackedName(freshName);
+ return freshName;
}
// From issue 7554. These should not be used on objects (as instance
@@ -54,7 +55,7 @@
// OK to use them as fields, as we only access fields directly if we know
// the receiver type.
static const List<String> _reservedNativeProperties = const <String>[
- 'Q', 'a', 'b', 'c', 'd', 'e', 'f', 'r', 'x', 'y', 'z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'r', 'x', 'y', 'z', 'Q',
// 2-letter:
'ch', 'cx', 'cy', 'db', 'dx', 'dy', 'fr', 'fx', 'fy', 'go', 'id', 'k1',
'k2', 'k3', 'k4', 'r1', 'r2', 'rx', 'ry', 'x1', 'x2', 'y1', 'y2',
@@ -192,7 +193,7 @@
/// Instance members starting with g and s are reserved for getters and
/// setters.
- bool _hasBannedPrefix(String name) {
+ static bool _hasBannedPrefix(String name) {
int code = name.codeUnitAt(0);
return code == $g || code == $s;
}
@@ -255,3 +256,96 @@
}
}
+/// Implements naming for constructor bodies.
+///
+/// Constructor bodies are only called in settings where the target is
+/// statically known. Therefore, we can share their names between classes.
+/// However, to support calling the constructor body of a super constructor,
+/// each level in the inheritance tree has to use its own names.
+///
+/// This class implements a naming scheme by counting the distance from
+/// a given constructor to [Object], where distance is the number of
+/// constructors declared along the inheritance chain.
+class _ConstructorBodyNamingScope {
+ final int _startIndex;
+ final List _constructors;
+
+ int get numberOfConstructors => _constructors.length;
+
+ _ConstructorBodyNamingScope _superScope;
+
+ _ConstructorBodyNamingScope.rootScope(ClassElement cls)
+ : _superScope = null,
+ _startIndex = 0,
+ _constructors = cls.constructors.toList(growable: false);
+
+ _ConstructorBodyNamingScope.forClass(ClassElement cls,
+ _ConstructorBodyNamingScope superScope)
+ : _superScope = superScope,
+ _startIndex = superScope._startIndex + superScope.numberOfConstructors,
+ _constructors = cls.constructors.toList(growable: false);
+
+ // Mixin Applications have constructors but we never generate code for them,
+ // so they do not count in the inheritance chain.
+ _ConstructorBodyNamingScope.forMixinApplication(ClassElement cls,
+ _ConstructorBodyNamingScope superScope)
+ : _superScope = superScope,
+ _startIndex = superScope._startIndex + superScope.numberOfConstructors,
+ _constructors = const [];
+
+ factory _ConstructorBodyNamingScope(ClassElement cls,
+ Map<ClassElement, _ConstructorBodyNamingScope> registry) {
+ return registry.putIfAbsent(cls, () {
+ if (cls.superclass == null) {
+ return new _ConstructorBodyNamingScope.rootScope(cls);
+ } else if (cls.isMixinApplication) {
+ return new _ConstructorBodyNamingScope.forMixinApplication(cls,
+ new _ConstructorBodyNamingScope(cls.superclass, registry));
+ } else {
+ return new _ConstructorBodyNamingScope.forClass(cls,
+ new _ConstructorBodyNamingScope(cls.superclass, registry));
+ }
+ });
+ }
+
+ String constructorBodyKeyFor(ConstructorBodyElement body) {
+ int position = _constructors.indexOf(body.constructor);
+ assert(invariant(body, position >= 0, message: "constructor body missing"));
+ return "@constructorBody@${_startIndex + position}";
+ }
+}
+
+abstract class _MinifyConstructorBodyNamer implements Namer {
+ Map<ClassElement, _ConstructorBodyNamingScope> _constructorBodyScopes =
+ new Map<ClassElement, _ConstructorBodyNamingScope>();
+
+ @override
+ jsAst.Name constructorBodyName(FunctionElement method) {
+ _ConstructorBodyNamingScope scope =
+ new _ConstructorBodyNamingScope(method.enclosingClass,
+ _constructorBodyScopes);
+ String key = scope.constructorBodyKeyFor(method);
+ return _disambiguateMemberByKey(key,
+ () => _proposeNameForConstructorBody(method));
+ }
+}
+
+abstract class _MinifiedOneShotInterceptorNamer implements Namer {
+ /// Property name used for the one-shot interceptor method for the given
+ /// [selector] and return-type specialization.
+ @override
+ jsAst.Name nameForGetOneShotInterceptor(Selector selector,
+ Iterable<ClassElement> classes) {
+ String root = selector.isOperator
+ ? operatorNameToIdentifier(selector.name)
+ : privateName(selector.memberName);
+ String prefix = selector.isGetter
+ ? r"$get"
+ : selector.isSetter ? r"$set" : "";
+ String arity = selector.isCall ? "${selector.argumentCount}" : "";
+ String suffix = suffixForGetInterceptor(classes);
+ String fullName = "\$intercepted$prefix\$$root$arity\$$suffix";
+ return _disambiguateInternalGlobal(fullName);
+ }
+}
+
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index 1dac9f9..9ce0b81 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -387,6 +387,20 @@
final Map<String, jsAst.Name> userInstanceOperators =
new HashMap<String, jsAst.Name>();
+ /// Used to disambiguate names for constants in [constantName].
+ final Set<String> usedConstantNames = new Set<String>();
+
+ Set<String> getUsedNames(NamingScope scope) {
+ if (scope == NamingScope.global) {
+ return usedGlobalNames;
+ } else if (scope == NamingScope.instance){
+ return usedInstanceNames;
+ } else {
+ assert(scope == NamingScope.constant);
+ return usedConstantNames;
+ }
+ }
+
final Map<String, int> popularNameCounters = <String, int>{};
final Map<LibraryElement, String> libraryLongNames =
@@ -413,6 +427,18 @@
final Map<String, String> suggestedGlobalNames = <String, String>{};
final Map<String, String> suggestedInstanceNames = <String, String>{};
+ Map<String, String> getSuggestedNames(NamingScope scope) {
+ if (scope == NamingScope.global) {
+ return suggestedGlobalNames;
+ } else if (scope == NamingScope.instance) {
+ return suggestedInstanceNames;
+ } else {
+ assert(scope == NamingScope.constant);
+ return const {};
+ }
+ }
+
+
/// Used to store unique keys for library names. Keys are not used as names,
/// nor are they visible in the output. The only serve as an internal
/// key into maps.
@@ -505,7 +531,7 @@
jsAst.Name result = constantNames[constant];
if (result == null) {
String longName = constantLongName(constant);
- result = getFreshName(longName, usedGlobalNames, suggestedGlobalNames);
+ result = getFreshName(NamingScope.constant, longName);
constantNames[constant] = result;
}
return result;
@@ -585,11 +611,16 @@
return '$name\$${suffix.join(r'$')}';
}
+ /// Name for a constructor body.
+ jsAst.Name constructorBodyName(FunctionElement ctor) {
+ return _disambiguateInternalMember(ctor,
+ () => _proposeNameForConstructorBody(ctor));
+ }
+
/// Annotated name for [method] encoding arity and named parameters.
jsAst.Name instanceMethodName(FunctionElement method) {
if (method.isGenerativeConstructorBody) {
- return _disambiguateInternalMember(method,
- () => _proposeNameForConstructorBody(method));
+ return constructorBodyName(method);
}
return invocationName(new Selector.fromElement(method));
}
@@ -821,7 +852,7 @@
jsAst.Name _disambiguateInternalGlobal(String name) {
jsAst.Name newName = internalGlobals[name];
if (newName == null) {
- newName = getFreshName(name, usedGlobalNames, suggestedGlobalNames);
+ newName = getFreshName(NamingScope.global, name);
internalGlobals[name] = newName;
}
return newName;
@@ -868,8 +899,7 @@
jsAst.Name newName = userGlobals[element];
if (newName == null) {
String proposedName = _proposeNameForGlobal(element);
- newName = getFreshName(proposedName, usedGlobalNames,
- suggestedGlobalNames);
+ newName = getFreshName(NamingScope.global, proposedName);
userGlobals[element] = newName;
}
return newName;
@@ -909,8 +939,30 @@
// proposed name must be a valid identifier, but not necessarily unique.
proposedName += r'$' + suffixes.join(r'$');
}
- newName = getFreshName(proposedName,
- usedInstanceNames, suggestedInstanceNames,
+ newName = getFreshName(NamingScope.instance, proposedName,
+ sanitizeForAnnotations: true);
+ userInstanceMembers[key] = newName;
+ }
+ return newName;
+ }
+
+ /// Returns the disambiguated name for the instance member identified by
+ /// [key].
+ ///
+ /// When a name for an element is requested by key, it may not be requested
+ /// by element at the same time, as two different names would be returned.
+ ///
+ /// If key has not yet been registered, [proposeName] is used to generate
+ /// a name proposal for the given key.
+ ///
+ /// [key] must not clash with valid instance names. This is typically
+ /// achieved by using at least one character in [key] that is not valid in
+ /// identifiers, for example the @ symbol.
+ jsAst.Name _disambiguateMemberByKey(String key, String proposeName()) {
+ jsAst.Name newName = userInstanceMembers[key];
+ if (newName == null) {
+ String name = proposeName();
+ newName = getFreshName(NamingScope.instance, name,
sanitizeForAnnotations: true);
userInstanceMembers[key] = newName;
}
@@ -949,8 +1001,7 @@
if (newName == null) {
String name = proposeName();
bool mayClashNative = _isUserClassExtendingNative(element.enclosingClass);
- newName = getFreshName(name,
- usedInstanceNames, suggestedInstanceNames,
+ newName = getFreshName(NamingScope.instance, name,
sanitizeForAnnotations: true,
sanitizeForNatives: mayClashNative);
internalInstanceMembers[element] = newName;
@@ -967,30 +1018,17 @@
jsAst.Name _disambiguateOperator(String operatorIdentifier) {
jsAst.Name newName = userInstanceOperators[operatorIdentifier];
if (newName == null) {
- newName = getFreshName(operatorIdentifier, usedInstanceNames,
- suggestedInstanceNames);
+ newName = getFreshName(NamingScope.instance, operatorIdentifier);
userInstanceOperators[operatorIdentifier] = newName;
}
return newName;
}
- /// Returns an unused name.
- ///
- /// [proposedName] must be a valid JavaScript identifier.
- ///
- /// If [sanitizeForAnnotations] is `true`, then the result is guaranteed not
- /// to have the form of an annotated name.
- ///
- /// If [sanitizeForNatives] it `true`, then the result is guaranteed not to
- /// clash with a property name on a native object.
- ///
- /// Note that [MinifyNamer] overrides this method with one that produces
- /// minified names.
- jsAst.Name getFreshName(String proposedName,
- Set<String> usedNames,
- Map<String, String> suggestedNames,
- {bool sanitizeForAnnotations: false,
- bool sanitizeForNatives: false}) {
+ String _generateFreshStringForName(String proposedName,
+ Set<String> usedNames,
+ Map<String, String> suggestedNames,
+ {bool sanitizeForAnnotations: false,
+ bool sanitizeForNatives: false}) {
if (sanitizeForAnnotations) {
proposedName = _sanitizeForAnnotations(proposedName);
}
@@ -1011,6 +1049,32 @@
candidate = "$proposedName$i";
}
usedNames.add(candidate);
+ return candidate;
+ }
+
+ /// Returns an unused name.
+ ///
+ /// [proposedName] must be a valid JavaScript identifier.
+ ///
+ /// If [sanitizeForAnnotations] is `true`, then the result is guaranteed not
+ /// to have the form of an annotated name.
+ ///
+ /// If [sanitizeForNatives] it `true`, then the result is guaranteed not to
+ /// clash with a property name on a native object.
+ ///
+ /// Note that [MinifyNamer] overrides this method with one that produces
+ /// minified names.
+ jsAst.Name getFreshName(NamingScope scope,
+ String proposedName,
+ {bool sanitizeForAnnotations: false,
+ bool sanitizeForNatives: false}) {
+ String candidate =
+ _generateFreshStringForName(proposedName,
+ getUsedNames(scope),
+ getSuggestedNames(scope),
+ sanitizeForAnnotations:
+ sanitizeForAnnotations,
+ sanitizeForNatives: sanitizeForNatives);
return new StringBackedName(candidate);
}
@@ -1340,8 +1404,7 @@
jsAst.Name getFunctionTypeName(FunctionType functionType) {
return functionTypeNameMap.putIfAbsent(functionType, () {
String proposedName = functionTypeNamer.computeName(functionType);
- return getFreshName(proposedName, usedInstanceNames,
- suggestedInstanceNames);
+ return getFreshName(NamingScope.instance, proposedName);
});
}
@@ -1482,133 +1545,6 @@
}
}
-abstract class _NamerName extends jsAst.Name {
- int get _kind;
-}
-
-class StringBackedName extends _NamerName {
- final String name;
- int get _kind => 1;
-
- StringBackedName(this.name);
-
- toString() => throw new UnsupportedError("Cannot convert a name to a string");
-
- operator==(other) {
- if (identical(this, other)) return true;
- return (other is StringBackedName) && other.name == name;
- }
-
- int get hashCode => name.hashCode;
-
- int compareTo(_NamerName other) {
- if (other._kind != _kind) return other._kind - _kind;
- return name.compareTo(other.name);
- }
-}
-
-abstract class _PrefixedName extends _NamerName {
- final jsAst.Name prefix;
- final jsAst.Name base;
- int get _kind;
-
- _PrefixedName(this.prefix, this.base);
-
- String get name => prefix.name + base.name;
-
- toString() => throw new UnsupportedError("Cannot convert a name to a string");
-
- bool operator==(other) {
- if (identical(this, other)) return true;
- if (other is! _PrefixedName) return false;
- return other.base == base && other.prefix == prefix;
- }
-
- int get hashCode => base.hashCode * 13 + prefix.hashCode;
-
- int compareTo(_NamerName other) {
- if (other._kind != _kind) return other._kind - _kind;
- _PrefixedName otherSameKind = other;
- int result = prefix.compareTo(otherSameKind.prefix);
- if (result == 0) {
- result = name.compareTo(otherSameKind.name);
- }
- return result;
- }
-}
-
-class GetterName extends _PrefixedName {
- int get _kind => 2;
-
- GetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
-}
-
-class SetterName extends _PrefixedName {
- int get _kind => 3;
-
- SetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
-}
-
-class _AsyncName extends _PrefixedName {
- int get _kind => 4;
-
- _AsyncName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
-
- @override
- bool get allowRename => true;
-}
-
-class CompoundName extends _NamerName {
- final List<_NamerName> _parts;
- int get _kind => 4;
- String _cachedName;
- int _cachedHashCode = -1;
-
- CompoundName(this._parts);
-
- String get name {
- if (_cachedName == null) {
- _cachedName = _parts.map((jsAst.Name name) => name.name).join();
- }
- return _cachedName;
- }
-
- toString() => throw new UnsupportedError("Cannot convert a name to a string");
-
- bool operator==(other) {
- if (identical(this, other)) return true;
- if (other is! CompoundName) return false;
- if (other._parts.length != _parts.length) return false;
- for (int i = 0; i < _parts.length; ++i) {
- if (other._parts[i] != _parts[i]) return false;
- }
- return true;
- }
-
- int get hashCode {
- if (_cachedHashCode < 0) {
- _cachedHashCode = 0;
- for (jsAst.Name name in _parts) {
- _cachedHashCode = (_cachedHashCode * 17 + name.hashCode) & 0x7fffffff;
- }
- }
- return _cachedHashCode;
- }
-
- int compareTo(_NamerName other) {
- if (other._kind != _kind) return other._kind - _kind;
- CompoundName otherSameKind = other;
- if (otherSameKind._parts.length != _parts.length) {
- return otherSameKind._parts.length - _parts.length;
- }
- int result = 0;
- for (int pos = 0; result == 0 && pos < _parts.length; pos++) {
- result = _parts[pos].compareTo(otherSameKind._parts[pos]);
- }
- return result;
- }
-}
-
/**
* Generator of names for [ConstantValue] values.
*
@@ -2044,3 +1980,9 @@
}
}
}
+
+enum NamingScope {
+ global,
+ instance,
+ constant
+}
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/js_backend/namer_names.dart b/pkg/compiler/lib/src/js_backend/namer_names.dart
new file mode 100644
index 0000000..3d0e1f3
--- /dev/null
+++ b/pkg/compiler/lib/src/js_backend/namer_names.dart
@@ -0,0 +1,181 @@
+// Copyright (c) 2015, 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.
+
+part of js_backend;
+
+abstract class _NamerName extends jsAst.Name {
+ int get _kind;
+
+ toString() => throw new UnsupportedError("Cannot convert a name to a string");
+}
+
+enum _NamerNameKinds {
+ StringBacked,
+ Getter,
+ Setter,
+ Async,
+ Compound,
+ Token
+}
+
+class StringBackedName extends _NamerName {
+ final String name;
+ int get _kind => _NamerNameKinds.StringBacked.index;
+
+ StringBackedName(this.name);
+
+ String get key => name;
+
+ operator==(other) {
+ if (identical(this, other)) return true;
+ return (other is StringBackedName) && other.name == name;
+ }
+
+ int get hashCode => name.hashCode;
+
+ int compareTo(_NamerName other) {
+ if (other._kind != _kind) return other._kind - _kind;
+ return name.compareTo(other.name);
+ }
+}
+
+abstract class _PrefixedName extends _NamerName implements jsAst.AstContainer {
+ final jsAst.Name prefix;
+ final jsAst.Name base;
+ int get _kind;
+
+ Iterable<jsAst.Node> get containedNodes => [prefix, base];
+
+ _PrefixedName(this.prefix, this.base);
+
+ String get name => prefix.name + base.name;
+
+ String get key => prefix.key + base.key;
+
+ bool operator==(other) {
+ if (identical(this, other)) return true;
+ if (other is! _PrefixedName) return false;
+ return other.base == base && other.prefix == prefix;
+ }
+
+ int get hashCode => base.hashCode * 13 + prefix.hashCode;
+
+ int compareTo(_NamerName other) {
+ if (other._kind != _kind) return other._kind - _kind;
+ _PrefixedName otherSameKind = other;
+ int result = prefix.compareTo(otherSameKind.prefix);
+ if (result == 0) {
+ result = prefix.compareTo(otherSameKind.prefix);
+ if (result == 0) {
+ result = base.compareTo(otherSameKind.base);
+ }
+ }
+ return result;
+ }
+}
+
+class GetterName extends _PrefixedName {
+ int get _kind => _NamerNameKinds.Getter.index;
+
+ GetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
+}
+
+class SetterName extends _PrefixedName {
+ int get _kind => _NamerNameKinds.Setter.index;
+
+ SetterName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
+}
+
+class _AsyncName extends _PrefixedName {
+ int get _kind => _NamerNameKinds.Async.index;
+
+ _AsyncName(jsAst.Name prefix, jsAst.Name base) : super(prefix, base);
+
+ @override
+ bool get allowRename => true;
+}
+
+class CompoundName extends _NamerName implements jsAst.AstContainer {
+ final List<_NamerName> _parts;
+ int get _kind => _NamerNameKinds.Compound.index;
+ String _cachedName;
+ int _cachedHashCode = -1;
+
+ Iterable<jsAst.Node> get containedNodes => _parts;
+
+ CompoundName(this._parts);
+
+ String get name {
+ if (_cachedName == null) {
+ _cachedName = _parts.map((jsAst.Name name) => name.name).join();
+ }
+ return _cachedName;
+ }
+
+ String get key => _parts.map((_NamerName name) => name.key).join();
+
+ bool operator==(other) {
+ if (identical(this, other)) return true;
+ if (other is! CompoundName) return false;
+ if (other._parts.length != _parts.length) return false;
+ for (int i = 0; i < _parts.length; ++i) {
+ if (other._parts[i] != _parts[i]) return false;
+ }
+ return true;
+ }
+
+ int get hashCode {
+ if (_cachedHashCode < 0) {
+ _cachedHashCode = 0;
+ for (jsAst.Name name in _parts) {
+ _cachedHashCode = (_cachedHashCode * 17 + name.hashCode) & 0x7fffffff;
+ }
+ }
+ return _cachedHashCode;
+ }
+
+ int compareTo(_NamerName other) {
+ if (other._kind != _kind) return other._kind - _kind;
+ CompoundName otherSameKind = other;
+ if (otherSameKind._parts.length != _parts.length) {
+ return otherSameKind._parts.length - _parts.length;
+ }
+ int result = 0;
+ for (int pos = 0; result == 0 && pos < _parts.length; pos++) {
+ result = _parts[pos].compareTo(otherSameKind._parts[pos]);
+ }
+ return result;
+ }
+}
+
+class TokenName extends _NamerName implements jsAst.ReferenceCountedAstNode {
+ int get _kind => _NamerNameKinds.Token.index;
+ String _name;
+ final String key;
+ final TokenScope _scope;
+ int _rc = 0;
+
+ TokenName(this._scope, this.key);
+
+ bool get isFinalized => _name != null;
+
+ String get name {
+ assert(isFinalized);
+ return _name;
+ }
+
+ @override
+ int compareTo(_NamerName other) {
+ if (other._kind != _kind) return other._kind - _kind;
+ TokenName otherToken = other;
+ return key.compareTo(otherToken.key);
+ }
+
+ markSeen(jsAst.TokenCounter counter) => _rc++;
+
+ finalize() {
+ assert(!isFinalized);
+ _name = _scope.getNextName();
+ }
+}
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
index 366d769..53e1130 100644
--- a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
@@ -220,52 +220,49 @@
Compiler compiler = backend.compiler;
Element closureFromTearOff = backend.findHelper('closureFromTearOff');
- String tearOffAccessText;
jsAst.Expression tearOffAccessExpression;
- String tearOffGlobalObjectName;
- String tearOffGlobalObject;
+ jsAst.Expression tearOffGlobalObjectString;
+ jsAst.Expression tearOffGlobalObject;
if (closureFromTearOff != null) {
- // We need both the AST that references [closureFromTearOff] and a string
- // for the NoCsp version that constructs a function.
tearOffAccessExpression =
backend.emitter.staticFunctionAccess(closureFromTearOff);
- tearOffAccessText =
- jsAst.prettyPrint(tearOffAccessExpression, compiler).getText();
- tearOffGlobalObjectName = tearOffGlobalObject =
- namer.globalObjectFor(closureFromTearOff);
+ tearOffGlobalObject =
+ js.stringPart(namer.globalObjectFor(closureFromTearOff));
+ tearOffGlobalObjectString =
+ js.string(namer.globalObjectFor(closureFromTearOff));
} else {
// Default values for mocked-up test libraries.
- tearOffAccessText =
- r'''function() { throw "Helper 'closureFromTearOff' missing." }''';
- tearOffAccessExpression = js(tearOffAccessText);
- tearOffGlobalObjectName = 'MissingHelperFunction';
- tearOffGlobalObject = '($tearOffAccessText())';
+ tearOffAccessExpression = js(
+ r'''function() { throw "Helper 'closureFromTearOff' missing." }''');
+ tearOffGlobalObjectString = js.string('MissingHelperFunction');
+ tearOffGlobalObject = js(
+ r'''(function() { throw "Helper 'closureFromTearOff' missing." })()''');
}
jsAst.Statement tearOffGetter;
if (!compiler.useContentSecurityPolicy) {
- // This template is uncached because it is constructed from code fragments
- // that can change from compilation to compilation. Some of these could be
- // avoided, except for the string literals that contain the compiled access
- // path to 'closureFromTearOff'.
- tearOffGetter = js.uncachedStatementTemplate('''
+ jsAst.Expression tearOffAccessText =
+ new jsAst.UnparsedNode(tearOffAccessExpression, compiler, false);
+ tearOffGetter = js.statement('''
function tearOffGetter(funcs, reflectionInfo, name, isIntercepted) {
return isIntercepted
? new Function("funcs", "reflectionInfo", "name",
- "$tearOffGlobalObjectName", "c",
+ #tearOffGlobalObjectString, "c",
"return function tearOff_" + name + (functionCounter++) + "(x) {" +
- "if (c === null) c = $tearOffAccessText(" +
+ "if (c === null) c = " + #tearOffAccessText + "(" +
"this, funcs, reflectionInfo, false, [x], name);" +
"return new c(this, funcs[0], x, name);" +
- "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null)
+ "}")(funcs, reflectionInfo, name, #tearOffGlobalObject, null)
: new Function("funcs", "reflectionInfo", "name",
- "$tearOffGlobalObjectName", "c",
+ #tearOffGlobalObjectString, "c",
"return function tearOff_" + name + (functionCounter++)+ "() {" +
- "if (c === null) c = $tearOffAccessText(" +
+ "if (c === null) c = " + #tearOffAccessText + "(" +
"this, funcs, reflectionInfo, false, [], name);" +
"return new c(this, funcs[0], null, name);" +
- "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null);
-}''').instantiate([]);
+ "}")(funcs, reflectionInfo, name, #tearOffGlobalObject, null);
+}''', {'tearOffAccessText': tearOffAccessText,
+ 'tearOffGlobalObject': tearOffGlobalObject,
+ 'tearOffGlobalObjectString': tearOffGlobalObjectString});
} else {
tearOffGetter = js.statement('''
function tearOffGetter(funcs, reflectionInfo, name, isIntercepted) {
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index 6f3f30e..b8fa032 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -4,7 +4,7 @@
part of dart2js.js_emitter;
-const USE_NEW_EMITTER = const bool.fromEnvironment("dart2js.use.new.emitter");
+const USE_LAZY_EMITTER = const bool.fromEnvironment("dart2js.use.lazy.emitter");
/**
* Generates the code for all used classes in the program. Static fields (even
@@ -22,6 +22,7 @@
Emitter emitter;
final Set<ClassElement> neededClasses = new Set<ClassElement>();
+ Set<ClassElement> classesOnlyNeededForRti;
final Map<OutputUnit, List<ClassElement>> outputClassLists =
new Map<OutputUnit, List<ClassElement>>();
final Map<OutputUnit, List<ConstantValue>> outputConstantLists =
@@ -54,8 +55,8 @@
this.typeTestRegistry = new TypeTestRegistry(compiler) {
nativeEmitter = new NativeEmitter(this);
oldEmitter = new OldEmitter(compiler, namer, generateSourceMap, this);
- emitter = USE_NEW_EMITTER
- ? new new_js_emitter.Emitter(compiler, namer, nativeEmitter)
+ emitter = USE_LAZY_EMITTER
+ ? new lazy_js_emitter.Emitter(compiler, namer, nativeEmitter)
: oldEmitter;
metadataCollector = new MetadataCollector(compiler, emitter);
}
@@ -211,7 +212,7 @@
}
}
for (ClassElement cls in neededClasses) {
- final onlyForRti = typeTestRegistry.rtiNeededClasses.contains(cls);
+ final onlyForRti = classesOnlyNeededForRti.contains(cls);
if (!onlyForRti) {
backend.retainMetadataOf(cls);
oldEmitter.classEmitter.visitFields(cls, false,
@@ -253,7 +254,7 @@
}
/// Compute all the classes and typedefs that must be emitted.
- void computeNeededDeclarations() {
+ void computeNeededDeclarations(Set<ClassElement> rtiNeededClasses) {
// Compute needed typedefs.
typedefsNeededForReflection = Elements.sortedByPosition(
compiler.world.allTypedefs
@@ -297,13 +298,9 @@
// these are thought to not have been instantiated, so we neeed to be able
// to identify them later and make sure we only emit "empty shells" without
// fields, etc.
- typeTestRegistry.computeRtiNeededClasses();
+ classesOnlyNeededForRti = rtiNeededClasses.difference(neededClasses);
- // TODO(floitsch): either change the name, or get the rti-classes
- // differently.
- typeTestRegistry.rtiNeededClasses.removeAll(neededClasses);
- // rtiNeededClasses now contains only the "empty shells".
- neededClasses.addAll(typeTestRegistry.rtiNeededClasses);
+ neededClasses.addAll(classesOnlyNeededForRti);
// TODO(18175, floitsch): remove once issue 18175 is fixed.
if (neededClasses.contains(backend.jsIntClass)) {
@@ -330,7 +327,7 @@
for (ClassElement element in sortedClasses) {
if (Elements.isNativeOrExtendsNative(element) &&
- !typeTestRegistry.rtiNeededClasses.contains(element)) {
+ !classesOnlyNeededForRti.contains(element)) {
// For now, native classes and related classes cannot be deferred.
nativeClassesAndSubclasses.add(element);
assert(invariant(element,
@@ -390,8 +387,10 @@
// Compute the required type checks to know which classes need a
// 'is$' method.
typeTestRegistry.computeRequiredTypeChecks();
+ // Compute the classes needed by RTI.
+ Set<ClassElement> rtiClasses = typeTestRegistry.computeRtiNeededClasses();
- computeNeededDeclarations();
+ computeNeededDeclarations(rtiClasses);
computeNeededConstants();
computeNeededStatics();
computeNeededStaticNonFinalFields();
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/class_builder.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/class_builder.dart
similarity index 100%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/class_builder.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/class_builder.dart
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
similarity index 98%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
index 47bd9ff..3a7ab57 100644
--- a/pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/class_emitter.dart
@@ -275,8 +275,9 @@
}
void emitNativeInfo(Class cls, ClassBuilder builder) {
- if (cls.nativeInfo != null) {
- builder.addPropertyByName(namer.nativeSpecProperty, cls.nativeInfo);
+ jsAst.Expression nativeInfo = NativeGenerator.encodeNativeInfo(cls);
+ if (nativeInfo != null) {
+ builder.addPropertyByName(namer.nativeSpecProperty, nativeInfo);
}
}
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/code_emitter_helper.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/code_emitter_helper.dart
similarity index 100%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/code_emitter_helper.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/code_emitter_helper.dart
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/container_builder.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/container_builder.dart
similarity index 100%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/container_builder.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/container_builder.dart
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/declarations.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
similarity index 100%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/declarations.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
similarity index 97%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
index 552fd27..ac0bd3a 100644
--- a/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
@@ -38,11 +38,6 @@
Map<OutputUnit, CodeOutput> outputBuffers = new Map<OutputUnit, CodeOutput>();
String classesCollector;
- Set<ClassElement> get neededClasses => task.neededClasses;
- Map<OutputUnit, List<ClassElement>> get outputClassLists
- => task.outputClassLists;
- Map<OutputUnit, List<ConstantValue>> get outputConstantLists
- => task.outputConstantLists;
final Map<jsAst.Name, String> mangledFieldNames =
new HashMap<jsAst.Name, String>();
final Map<jsAst.Name, String> mangledGlobalFieldNames =
@@ -204,7 +199,7 @@
/// Contains the global state that is needed to initialize and load a
/// deferred library.
- jsAst.Name get globalsHolder => namer.internalGlobal("globalsHolder");
+ String get globalsHolder => r"$globals$";
@override
jsAst.Expression generateEmbeddedGlobalAccess(String global) {
@@ -328,10 +323,10 @@
jsAst.Expression subclassReadGenerator(jsAst.Expression subclass),
jsAst.Expression interceptorsByTagAccess,
jsAst.Expression leafTagsAccess) {
- return nativeEmitter.buildNativeInfoHandler(infoAccess, constructorAccess,
- subclassReadGenerator,
- interceptorsByTagAccess,
- leafTagsAccess);
+ return NativeGenerator.buildNativeInfoHandler(infoAccess, constructorAccess,
+ subclassReadGenerator,
+ interceptorsByTagAccess,
+ leafTagsAccess);
}
jsAst.ObjectInitializer generateInterceptedNamesSet() {
@@ -576,32 +571,26 @@
List<jsAst.Expression> laziesInfo = buildLaziesInfo(lazyFields);
return js.statement('''
(function(lazies) {
- if (#notInMinifiedMode) {
- var descriptorLength = 4;
- } else {
- var descriptorLength = 3;
- }
-
- for (var i = 0; i < lazies.length; i += descriptorLength) {
- var fieldName = lazies [i];
- var getterName = lazies[i + 1];
- var lazyValue = lazies[i + 2];
- if (#notInMinifiedMode) {
- var staticName = lazies[i + 3];
+ for (var i = 0; i < lazies.length; ) {
+ var fieldName = lazies[i++];
+ var getterName = lazies[i++];
+ if (#notMinified) {
+ var staticName = lazies[i++];
}
+ var lazyValue = lazies[i++];
// We build the lazy-check here:
// lazyInitializer(fieldName, getterName, lazyValue, staticName);
// 'staticName' is used for error reporting in non-minified mode.
// 'lazyValue' must be a closure that constructs the initial value.
- if (#notInMinifiedMode) {
+ if (#notMinified) {
#lazy(fieldName, getterName, lazyValue, staticName);
} else {
#lazy(fieldName, getterName, lazyValue);
}
}
})(#laziesInfo)
- ''', {'notInMinifiedMode': !compiler.enableMinification,
+ ''', {'notMinified': !compiler.enableMinification,
'laziesInfo': new jsAst.ArrayInitializer(laziesInfo),
'lazy': js(lazyInitializerName)});
} else {
@@ -617,20 +606,17 @@
// initialized field after all because of constant folding
// before code generation.
if (code == null) continue;
- if (compiler.enableMinification) {
- laziesInfo.addAll([js.quoteName(namer.globalPropertyName(element)),
- js.quoteName(namer.lazyInitializerName(element)),
- code]);
- } else {
- laziesInfo.addAll([js.quoteName(namer.globalPropertyName(element)),
- js.quoteName(namer.lazyInitializerName(element)),
- code,
- js.string(element.name)]);
+ laziesInfo.add(js.quoteName(namer.globalPropertyName(element)));
+ laziesInfo.add(js.quoteName(namer.lazyInitializerName(element)));
+ if (!compiler.enableMinification) {
+ laziesInfo.add(js.string(element.name));
}
+ laziesInfo.add(code);
}
return laziesInfo;
}
+ // TODO(sra): Remove this unused function.
jsAst.Expression buildLazilyInitializedStaticField(
VariableElement element, {String isolateProperties}) {
jsAst.Expression code = backend.generatedCode[element];
@@ -742,12 +728,14 @@
}
jsAst.Statement buildFunctionThatReturnsNull() {
- return js.statement('# = function() {}',
- [backend.rti.getFunctionThatReturnsNullName]);
+ return js.statement('#.# = function() {}',
+ [namer.isolateName,
+ backend.rti.getFunctionThatReturnsNullName]);
}
jsAst.Expression generateFunctionThatReturnsNull() {
- return js("#", [backend.rti.getFunctionThatReturnsNullName]);
+ return js("#.#", [namer.isolateName,
+ backend.rti.getFunctionThatReturnsNullName]);
}
buildMain(jsAst.Statement invokeMain) {
@@ -887,6 +875,8 @@
if (#outputContainsConstantList) {
Isolate.#makeConstListProperty = oldIsolate.#makeConstListProperty;
}
+ Isolate.#functionThatReturnsNullProperty =
+ oldIsolate.#functionThatReturnsNullProperty;
if (#hasIncrementalSupport) {
Isolate.#lazyInitializerProperty =
oldIsolate.#lazyInitializerProperty;
@@ -904,6 +894,8 @@
'isolatePropertiesName': namer.isolatePropertiesName,
'outputContainsConstantList': task.outputContainsConstantList,
'makeConstListProperty': makeConstListProperty,
+ 'functionThatReturnsNullProperty':
+ backend.rti.getFunctionThatReturnsNullName,
'hasIncrementalSupport': compiler.hasIncrementalSupport,
'lazyInitializerProperty': lazyInitializerProperty,});
}
@@ -1632,7 +1624,7 @@
if (descriptors != null && descriptors.isNotEmpty) {
Iterable<LibraryElement> libraries =
- task.outputLibraryLists[outputUnit];
+ task.outputLibraryLists[outputUnit];
if (libraries == null) libraries = [];
// TODO(johnniwinther): Avoid creating [CodeBuffer]s.
@@ -1651,9 +1643,14 @@
void finalizeTokensInAst(jsAst.Program main,
Iterable<jsAst.Program> deferredParts) {
- task.metadataCollector.countTokensInAst(main);
- deferredParts.forEach(task.metadataCollector.countTokensInAst);
+ jsAst.TokenCounter counter = new jsAst.TokenCounter();
+ counter.countTokens(main);
+ deferredParts.forEach(counter.countTokens);
task.metadataCollector.finalizeTokens();
+ if (backend.namer is jsAst.TokenFinalizer) {
+ var finalizer = backend.namer;
+ finalizer.finalizeTokens();
+ }
}
int emitProgram(ProgramBuilder programBuilder) {
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/interceptor_emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/interceptor_emitter.dart
similarity index 100%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/interceptor_emitter.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/interceptor_emitter.dart
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/nsm_emitter.dart
similarity index 97%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/nsm_emitter.dart
index 134e6cf..5d7df35 100644
--- a/pkg/compiler/lib/src/js_emitter/old_emitter/nsm_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/nsm_emitter.dart
@@ -291,16 +291,17 @@
///
/// See [buildTrivialNsmHandlers].
class _DiffEncodedListOfNames extends jsAst.DeferredString
- implements AstContainer {
+ implements jsAst.AstContainer {
String _cachedValue;
- jsAst.ArrayInitializer ast;
+ List<jsAst.ArrayInitializer> ast;
+
+ Iterable<jsAst.Node> get containedNodes => ast;
_DiffEncodedListOfNames(Iterable<Iterable<jsAst.Name>> names) {
// Store the names in ArrayInitializer nodes to make them discoverable
// by traversals of the ast.
- ast = new jsAst.ArrayInitializer(
- names.map((Iterable i) => new jsAst.ArrayInitializer(i.toList()))
- .toList());
+ ast = names.map((Iterable i) => new jsAst.ArrayInitializer(i.toList()))
+ .toList();
}
void _computeDiffEncodingForList(Iterable<jsAst.Name> names,
@@ -367,7 +368,7 @@
String _computeDiffEncoding() {
StringBuffer buffer = new StringBuffer();
- for (jsAst.ArrayInitializer list in ast.elements) {
+ for (jsAst.ArrayInitializer list in ast) {
if (buffer.isNotEmpty) {
// Emit period that resets the diff base to zero when we switch to
// normal calling convention (this avoids the need to code negative
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/setup_program_builder.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/setup_program_builder.dart
similarity index 100%
rename from pkg/compiler/lib/src/js_emitter/old_emitter/setup_program_builder.dart
rename to pkg/compiler/lib/src/js_emitter/full_emitter/setup_program_builder.dart
diff --git a/pkg/compiler/lib/src/js_emitter/js_emitter.dart b/pkg/compiler/lib/src/js_emitter/js_emitter.dart
index 09d9d18..4049f43 100644
--- a/pkg/compiler/lib/src/js_emitter/js_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/js_emitter.dart
@@ -34,8 +34,7 @@
import '../hash/sha1.dart' show Hasher;
import '../js/js.dart' as jsAst;
-import '../js/js.dart' show
- js;
+import '../js/js.dart' show js;
import 'package:js_ast/src/precedence.dart' as js_precedence;
@@ -56,9 +55,9 @@
TypeVariableHandler;
import 'model.dart';
-import 'program_builder.dart';
+import 'program_builder/program_builder.dart';
-import 'new_emitter/emitter.dart' as new_js_emitter;
+import 'lazy_emitter/emitter.dart' as lazy_js_emitter;
import '../io/line_column_provider.dart' show
LineColumnCollector,
@@ -109,12 +108,12 @@
part 'runtime_type_generator.dart';
part 'type_test_registry.dart';
-part 'old_emitter/class_builder.dart';
-part 'old_emitter/class_emitter.dart';
-part 'old_emitter/code_emitter_helper.dart';
-part 'old_emitter/container_builder.dart';
-part 'old_emitter/declarations.dart';
-part 'old_emitter/emitter.dart';
-part 'old_emitter/interceptor_emitter.dart';
-part 'old_emitter/nsm_emitter.dart';
-part 'old_emitter/setup_program_builder.dart';
+part 'full_emitter/class_builder.dart';
+part 'full_emitter/class_emitter.dart';
+part 'full_emitter/code_emitter_helper.dart';
+part 'full_emitter/container_builder.dart';
+part 'full_emitter/declarations.dart';
+part 'full_emitter/emitter.dart';
+part 'full_emitter/interceptor_emitter.dart';
+part 'full_emitter/nsm_emitter.dart';
+part 'full_emitter/setup_program_builder.dart';
diff --git a/pkg/compiler/lib/src/js_emitter/new_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/lazy_emitter/emitter.dart
similarity index 98%
rename from pkg/compiler/lib/src/js_emitter/new_emitter/emitter.dart
rename to pkg/compiler/lib/src/js_emitter/lazy_emitter/emitter.dart
index b431c6d..0c3ae73 100644
--- a/pkg/compiler/lib/src/js_emitter/new_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/lazy_emitter/emitter.dart
@@ -9,7 +9,7 @@
METADATA,
TYPES;
-import '../program_builder.dart' show ProgramBuilder;
+import '../program_builder/program_builder.dart' show ProgramBuilder;
import '../model.dart';
import 'model_emitter.dart';
import '../../common.dart';
diff --git a/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/lazy_emitter/model_emitter.dart
similarity index 95%
rename from pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart
rename to pkg/compiler/lib/src/js_emitter/lazy_emitter/model_emitter.dart
index f34051c1..6a05e29 100644
--- a/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/lazy_emitter/model_emitter.dart
@@ -33,47 +33,6 @@
import '../js_emitter.dart' show NativeGenerator, buildTearOffCode;
import '../model.dart';
-/// Represents the LiteralString resulting from unparsing [expression]. The
-/// actual unparsing is done on demand when requesting the [value] of this
-/// node.
-class _UnparsedNode extends js.DeferredString
- implements AstContainer {
- @override
- final js.Node ast;
- final Compiler _compiler;
- final bool _protectForEval;
- js.LiteralString _cachedLiteral;
-
- /// A [js.Literal] that represents the string result of unparsing [ast].
- ///
- /// When its string [value] is requested, the node pretty-prints the given
- /// [ast] and, if [protectForEval] is true, wraps the resulting
- /// string in parenthesis. The result is also escaped.
- _UnparsedNode(this.ast, this._compiler, this._protectForEval);
-
- js.LiteralString get _literal {
- if (_cachedLiteral == null) {
- String text = js.prettyPrint(ast, _compiler).getText();
- if (_protectForEval) {
- if (ast is js.Fun) text = '($text)';
- if (ast is js.LiteralExpression) {
- js.LiteralExpression literalExpression = ast;
- String template = literalExpression.template;
- if (template.startsWith("function ") ||
- template.startsWith("{")) {
- text = '($text)';
- }
- }
- }
- _cachedLiteral = js.js.escapedString(text);
- }
- return _cachedLiteral;
- }
-
- @override
- String get value => _literal.value;
-}
-
class ModelEmitter {
final Compiler compiler;
final Namer namer;
@@ -185,9 +144,11 @@
js.Statement mainAst = emitMainFragment(program);
- fragmentsCode.forEach(program.metadataFinalizer.countTokensInAst);
- program.metadataFinalizer.countTokensInAst(mainAst);
- program.metadataFinalizer.finalizeTokens();
+ js.TokenCounter counter = new js.TokenCounter();
+ fragmentsCode.forEach(counter.countTokens);
+ counter.countTokens(mainAst);
+
+ program.finalizers.forEach((js.TokenFinalizer f) => f.finalizeTokens());
for (int i = 0; i < fragmentsCode.length; ++i) {
String code = js.prettyPrint(fragmentsCode[i], compiler).getText();
@@ -217,7 +178,7 @@
/// See [_UnparsedNode] for details.
js.Literal unparse(Compiler compiler, js.Node value,
{bool protectForEval: true}) {
- return new _UnparsedNode(value, compiler, protectForEval);
+ return new js.UnparsedNode(value, compiler, protectForEval);
}
String buildGeneratedBy(compiler) {
@@ -292,7 +253,7 @@
generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG);
js.Expression leafTagsAccess =
generateEmbeddedGlobalAccess(LEAF_TAGS);
- js.Statement nativeInfoHandler = nativeEmitter.buildNativeInfoHandler(
+ js.Statement nativeInfoHandler = NativeGenerator.buildNativeInfoHandler(
nativeInfoAccess,
constructorAccess,
subclassReadGenerator,
@@ -630,10 +591,11 @@
js.Literal name = js.quoteName(cls.name);
js.LiteralNumber holderIndex = js.number(cls.holder.index);
js.Expression emittedClass = emitClass(cls);
- if (cls.nativeInfo == null) {
+ js.Expression nativeInfo = NativeGenerator.encodeNativeInfo(cls);
+ if (nativeInfo == null) {
return [name, emittedClass, holderIndex];
} else {
- return [name, emittedClass, cls.nativeInfo, holderIndex];
+ return [name, emittedClass, nativeInfo, holderIndex];
}
});
diff --git a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
index 68ec03d..8ffda24 100644
--- a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
+++ b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
@@ -10,7 +10,7 @@
/// ast for a program.
/// [value] is the actual position, once they have been finalized.
abstract class _MetadataEntry extends jsAst.DeferredNumber
- implements Comparable {
+ implements Comparable, jsAst.ReferenceCountedAstNode {
jsAst.Expression get entry;
int get value;
int get _rc;
@@ -18,7 +18,7 @@
// Mark this entry as seen. On the first time this is seen, the visitor
// will be applied to the [entry] to also mark potential [_MetadataEntry]
// instances in the [entry] as seen.
- markSeen(jsAst.BaseVisitor visitor);
+ markSeen(jsAst.TokenCounter visitor);
}
class _BoundMetadataEntry extends _MetadataEntry {
@@ -107,7 +107,7 @@
int get precedenceLevel => js_precedence.PRIMARY;
}
-class MetadataCollector implements TokenFinalizer {
+class MetadataCollector implements jsAst.TokenFinalizer {
final Compiler _compiler;
final Emitter _emitter;
@@ -232,7 +232,10 @@
}
_MetadataEntry _addGlobalMetadata(jsAst.Node node) {
- String printed = jsAst.prettyPrint(node, _compiler).getText();
+ String nameToKey(jsAst.Name name) => "${name.key}";
+ String printed = jsAst.prettyPrint(node, _compiler,
+ renamerForNames: nameToKey)
+ .getText();
return _globalMetadataMap.putIfAbsent(printed, () {
_BoundMetadataEntry result = new _BoundMetadataEntry(node);
if (_compiler.hasIncrementalSupport) {
@@ -298,12 +301,6 @@
}
@override
- void countTokensInAst(jsAst.Node ast) {
- TokenCounter visitor = new TokenCounter();
- visitor.countTokens(ast);
- }
-
- @override
void finalizeTokens() {
bool checkTokensInTypes(OutputUnit outputUnit, entries) {
UnBoundDebugger debugger = new UnBoundDebugger(outputUnit);
@@ -316,7 +313,7 @@
return true;
}
void countTokensInTypes(Iterable<_BoundMetadataEntry> entries) {
- TokenCounter counter = new TokenCounter();
+ jsAst.TokenCounter counter = new jsAst.TokenCounter();
entries.where((_BoundMetadataEntry e) => e._rc > 0)
.map((_BoundMetadataEntry e) => e.entry)
.forEach(counter.countTokens);
@@ -360,39 +357,6 @@
}
}
-/// Interface for ast nodes that encapsulate an ast that needs to be
-/// traversed when counting tokens.
-///
-/// TODO(herhut): Find a shared place once namer also uses tokens.
-abstract class AstContainer implements jsAst.Node {
- jsAst.Node get ast;
-}
-
-abstract class TokenFinalizer {
- void countTokensInAst(jsAst.Node ast);
- void finalizeTokens();
-}
-
-class TokenCounter extends jsAst.BaseVisitor {
- @override
- visitNode(jsAst.Node node) {
- if (node is AstContainer) {
- node.ast.accept(this);
- } else {
- super.visitNode(node);
- }
- }
-
- @override
- visitDeferredNumber(jsAst.DeferredNumber token) {
- if (token is _MetadataEntry) {
- token.markSeen(this);
- }
- }
-
- void countTokens(jsAst.Node node) => node.accept(this);
-}
-
class UnBoundDebugger extends jsAst.BaseVisitor {
OutputUnit outputUnit;
bool _foundUnboundToken = false;
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index 8dec194..6b06eec 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -4,7 +4,8 @@
library dart2js.new_js_emitter.model;
-import '../js/js.dart' as js show Expression, Statement, Name, Literal;
+import '../js/js.dart' as js show Expression, Statement, Name, Literal,
+ TokenFinalizer;
import '../constants/values.dart' show ConstantValue;
import '../deferred_load.dart' show OutputUnit;
@@ -29,13 +30,14 @@
// TODO(floitsch): we should store the metadata directly instead of storing
// the collector. However, the old emitter still updates the data.
final MetadataCollector _metadataCollector;
- TokenFinalizer get metadataFinalizer => _metadataCollector;
+ final Iterable<js.TokenFinalizer> finalizers;
Program(this.fragments,
this.holders,
this.loadMap,
this.typeToInterceptorMap,
this._metadataCollector,
+ this.finalizers,
{this.needsNativeSupport,
this.outputContainsConstantList,
this.hasIsolateSupport}) {
@@ -241,8 +243,14 @@
/// Whether the class must be evaluated eagerly.
bool isEager = false;
- /// Data that must be emitted with the class for native interop.
- js.Literal nativeInfo;
+ /// Leaf tags. See [NativeEmitter.prepareNativeClasses].
+ List<String> nativeLeafTags;
+
+ /// Non-leaf tags. See [NativeEmitter.prepareNativeClasses].
+ List<String> nativeNonLeafTags;
+
+ /// Native extensions. See [NativeEmitter.prepareNativeClasses].
+ List<Class> nativeExtensions;
Class(this.element, this.name, this.holder,
this.methods,
diff --git a/pkg/compiler/lib/src/js_emitter/native_emitter.dart b/pkg/compiler/lib/src/js_emitter/native_emitter.dart
index 3e7b649..56bb8de 100644
--- a/pkg/compiler/lib/src/js_emitter/native_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/native_emitter.dart
@@ -30,6 +30,7 @@
cachedBuilders = emitterTask.compiler.cacheStrategy.newMap();
Compiler get compiler => emitterTask.compiler;
+
JavaScriptBackend get backend => compiler.backend;
jsAst.Expression get defPropFunction {
@@ -43,8 +44,9 @@
* Removes trivial classes (that can be represented by a super type) and
* generates properties that have to be added to classes (native or not).
*
- * Updates the `nativeInfo` field of the given classes. This data
- * must be emitted with the corresponding classes.
+ * Updates the `nativeLeafTags`, `nativeNonLeafTags` and `nativeExtensions`
+ * fields of the given classes. This data must be emitted with the
+ * corresponding classes.
*
* The interceptors are filtered to avoid emitting trivial interceptors. For
* example, if the program contains no code that can distinguish between the
@@ -73,6 +75,7 @@
Class objectClass = null;
Class jsInterceptorClass = null;
+
void walk(Class cls) {
if (cls.element == compiler.objectClass) {
objectClass = cls;
@@ -94,7 +97,7 @@
// needed class.
Set<Class> neededClasses = new Set<Class>();
- Set<Class> nonleafClasses = new Set<Class>();
+ Set<Class> nonLeafClasses = new Set<Class>();
Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder);
@@ -130,13 +133,13 @@
if (cls.isNative &&
native.nativeTagsForcedNonLeaf(classElement)) {
needed = true;
- nonleafClasses.add(cls);
+ nonLeafClasses.add(cls);
}
if (needed || neededClasses.contains(cls)) {
neededClasses.add(cls);
neededClasses.add(cls.superclass);
- nonleafClasses.add(cls.superclass);
+ nonLeafClasses.add(cls.superclass);
}
}
@@ -149,7 +152,7 @@
if (!cls.isNative) continue;
List<String> nativeTags = native.nativeTagsOfClass(cls.element);
- if (nonleafClasses.contains(cls) ||
+ if (nonLeafClasses.contains(cls) ||
extensionPoints.containsKey(cls)) {
nonleafTags
.putIfAbsent(cls, () => new Set<String>())
@@ -168,51 +171,26 @@
}
}
+ void fillNativeInfo(Class cls) {
+ assert(cls.nativeLeafTags == null &&
+ cls.nativeNonLeafTags == null &&
+ cls.nativeExtensions == null);
+ if (leafTags[cls] != null) {
+ cls.nativeLeafTags = leafTags[cls].toList(growable: false);
+ }
+ if (nonleafTags[cls] != null) {
+ cls.nativeNonLeafTags = nonleafTags[cls].toList(growable: false);
+ }
+ cls.nativeExtensions = extensionPoints[cls];
+ }
// Add properties containing the information needed to construct maps used
// by getNativeInterceptor and custom elements.
if (compiler.enqueuer.codegen.nativeEnqueuer
.hasInstantiatedNativeClasses()) {
- void generateClassInfo(Class cls) {
- // Property has the form:
- //
- // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...",
- //
- // If there is no data following a semicolon, the semicolon can be
- // omitted.
-
- String formatTags(Iterable<String> tags) {
- if (tags == null) return '';
- return (tags.toList()..sort()).join('|');
- }
-
- List<Class> extensions = extensionPoints[cls];
-
- String leafStr = formatTags(leafTags[cls]);
- String nonleafStr = formatTags(nonleafTags[cls]);
-
- StringBuffer sb = new StringBuffer(leafStr);
- if (nonleafStr != '') {
- sb..write(';')..write(nonleafStr);
- }
-
- String encoding = sb.toString();
-
- if (cls.isNative || encoding != '' || extensions != null) {
- List<jsAst.Literal> parts = <jsAst.Literal>[js.stringPart(encoding)];
- if (extensions != null) {
- parts..add(js.stringPart(';'))
- ..addAll(
- js.joinLiterals(extensions.map((Class cls) => cls.name),
- js.stringPart('|')));
- }
- assert(cls.nativeInfo == null);
- cls.nativeInfo = js.concatenateStrings(parts, addQuotes: true);
- }
- }
- generateClassInfo(jsInterceptorClass);
+ fillNativeInfo(jsInterceptorClass);
for (Class cls in classes) {
if (!cls.isNative || neededClasses.contains(cls)) {
- generateClassInfo(cls);
+ fillNativeInfo(cls);
}
}
}
@@ -367,96 +345,4 @@
if (Elements.isNativeOrExtendsNative(cls)) return true;
return isSupertypeOfNativeClass(element);
}
-
- /// Returns a JavaScript template that fills the embedded globals referenced
- /// by [interceptorsByTagAccess] and [leafTagsAccess].
- ///
- /// This code must be invoked for every class that has a native info before
- /// the program starts.
- ///
- /// The [infoAccess] parameter must evaluate to an expression that contains
- /// the info (as a JavaScript string).
- ///
- /// The [constructorAccess] parameter must evaluate to an expression that
- /// contains the constructor of the class. The constructor's prototype must
- /// be set up.
- ///
- /// The [subclassReadGenerator] function must evaluate to a JS expression
- /// that returns a reference to the constructor (with evaluated prototype)
- /// of the given JS expression.
- ///
- /// The [interceptorsByTagAccess] must point to the embedded global
- /// [embeddedNames.INTERCEPTORS_BY_TAG] and must be initialized with an empty
- /// JS Object (used as a map).
- ///
- /// Similarly, the [leafTagsAccess] must point to the embedded global
- /// [embeddedNames.LEAF_TAGS] and must be initialized with an empty JS Object
- /// (used as a map).
- ///
- /// Both variables are passed in (instead of creating the access here) to
- /// make sure the caller is aware of these globals.
- jsAst.Statement buildNativeInfoHandler(
- jsAst.Expression infoAccess,
- jsAst.Expression constructorAccess,
- jsAst.Expression subclassReadGenerator(jsAst.Expression subclass),
- jsAst.Expression interceptorsByTagAccess,
- jsAst.Expression leafTagsAccess) {
- jsAst.Expression subclassRead =
- subclassReadGenerator(js('subclasses[i]', []));
- return js.statement('''
- // The native info looks like this:
- //
- // HtmlElement: {
- // "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton"
- //
- // The first two semicolon-separated parts contain dispatch tags, the
- // third contains the JavaScript names for classes.
- //
- // The tags indicate that JavaScript objects with the dispatch tags
- // (usually constructor names) HTMLDivElement, HTMLAnchorElement and
- // HTMLElement all map to the Dart native class named HtmlElement.
- // The first set is for effective leaf nodes in the hierarchy, the
- // second set is non-leaf nodes.
- //
- // The third part contains the JavaScript names of Dart classes that
- // extend the native class. Here, FancyButton extends HtmlElement, so
- // the runtime needs to know that window.HTMLElement.prototype is the
- // prototype that needs to be extended in creating the custom element.
- //
- // The information is used to build tables referenced by
- // getNativeInterceptor and custom element support.
- {
- var nativeSpec = #info.split(";");
- if (nativeSpec[0]) {
- var tags = nativeSpec[0].split("|");
- for (var i = 0; i < tags.length; i++) {
- #interceptorsByTagAccess[tags[i]] = #constructor;
- #leafTagsAccess[tags[i]] = true;
- }
- }
- if (nativeSpec[1]) {
- tags = nativeSpec[1].split("|");
- if (#allowNativesSubclassing) {
- if (nativeSpec[2]) {
- var subclasses = nativeSpec[2].split("|");
- for (var i = 0; i < subclasses.length; i++) {
- var subclass = #subclassRead;
- subclass.#nativeSuperclassTagName = tags[0];
- }
- }
- for (i = 0; i < tags.length; i++) {
- #interceptorsByTagAccess[tags[i]] = #constructor;
- #leafTagsAccess[tags[i]] = false;
- }
- }
- }
- }
- ''', {'info': infoAccess,
- 'constructor': constructorAccess,
- 'subclassRead': subclassRead,
- 'interceptorsByTagAccess': interceptorsByTagAccess,
- 'leafTagsAccess': leafTagsAccess,
- 'nativeSuperclassTagName': embeddedNames.NATIVE_SUPERCLASS_TAG_NAME,
- 'allowNativesSubclassing': true});
- }
-}
+}
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/js_emitter/native_generator.dart b/pkg/compiler/lib/src/js_emitter/native_generator.dart
index 60f6c3d..9a4145c 100644
--- a/pkg/compiler/lib/src/js_emitter/native_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/native_generator.dart
@@ -73,4 +73,141 @@
// TODO(sra): MD5 of contributing source code or URIs?
return 'ZxYxX';
}
+
+ /// Encodes the collected native information so that it can be treated by
+ /// the native info-handler below.
+ ///
+ /// The encoded information has the form:
+ ///
+ // "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...",
+ //
+ // If there is no data following a semicolon, the semicolon can be omitted.
+ static jsAst.Expression encodeNativeInfo(Class cls) {
+ List<String> leafTags = cls.nativeLeafTags;
+ List<String> nonLeafTags = cls.nativeNonLeafTags;
+ List<Class> extensions = cls.nativeExtensions;
+
+ String formatTags(Iterable<String> tags) {
+ if (tags == null) return '';
+ return (tags.toList()
+ ..sort()).join('|');
+ }
+
+ String leafStr = formatTags(leafTags);
+ String nonLeafStr = formatTags(nonLeafTags);
+
+ StringBuffer sb = new StringBuffer(leafStr);
+ if (nonLeafStr != '') {
+ sb
+ ..write(';')
+ ..write(nonLeafStr);
+ }
+
+ String encoding = sb.toString();
+
+ if (cls.isNative || encoding != '' || extensions != null) {
+ List<jsAst.Literal> parts = <jsAst.Literal>[js.stringPart(encoding)];
+ if (extensions != null) {
+ parts
+ ..add(js.stringPart(';'))
+ ..addAll(
+ js.joinLiterals(extensions.map((Class cls) => cls.name),
+ js.stringPart('|')));
+ }
+ return jsAst.concatenateStrings(parts, addQuotes: true);
+ }
+ return null;
+ }
+
+ /// Returns a JavaScript template that fills the embedded globals referenced
+ /// by [interceptorsByTagAccess] and [leafTagsAccess].
+ ///
+ /// This code must be invoked for every class that has a native info before
+ /// the program starts.
+ ///
+ /// The [infoAccess] parameter must evaluate to an expression that contains
+ /// the info (as a JavaScript string).
+ ///
+ /// The [constructorAccess] parameter must evaluate to an expression that
+ /// contains the constructor of the class. The constructor's prototype must
+ /// be set up.
+ ///
+ /// The [subclassReadGenerator] function must evaluate to a JS expression
+ /// that returns a reference to the constructor (with evaluated prototype)
+ /// of the given JS expression.
+ ///
+ /// The [interceptorsByTagAccess] must point to the embedded global
+ /// [embeddedNames.INTERCEPTORS_BY_TAG] and must be initialized with an empty
+ /// JS Object (used as a map).
+ ///
+ /// Similarly, the [leafTagsAccess] must point to the embedded global
+ /// [embeddedNames.LEAF_TAGS] and must be initialized with an empty JS Object
+ /// (used as a map).
+ ///
+ /// Both variables are passed in (instead of creating the access here) to
+ /// make sure the caller is aware of these globals.
+ static jsAst.Statement buildNativeInfoHandler(
+ jsAst.Expression infoAccess,
+ jsAst.Expression constructorAccess,
+ jsAst.Expression subclassReadGenerator(jsAst.Expression subclass),
+ jsAst.Expression interceptorsByTagAccess,
+ jsAst.Expression leafTagsAccess) {
+ jsAst.Expression subclassRead =
+ subclassReadGenerator(js('subclasses[i]', []));
+ return js.statement('''
+ // The native info looks like this:
+ //
+ // HtmlElement: {
+ // "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton"
+ //
+ // The first two semicolon-separated parts contain dispatch tags, the
+ // third contains the JavaScript names for classes.
+ //
+ // The tags indicate that JavaScript objects with the dispatch tags
+ // (usually constructor names) HTMLDivElement, HTMLAnchorElement and
+ // HTMLElement all map to the Dart native class named HtmlElement.
+ // The first set is for effective leaf nodes in the hierarchy, the
+ // second set is non-leaf nodes.
+ //
+ // The third part contains the JavaScript names of Dart classes that
+ // extend the native class. Here, FancyButton extends HtmlElement, so
+ // the runtime needs to know that window.HTMLElement.prototype is the
+ // prototype that needs to be extended in creating the custom element.
+ //
+ // The information is used to build tables referenced by
+ // getNativeInterceptor and custom element support.
+ {
+ var nativeSpec = #info.split(";");
+ if (nativeSpec[0]) {
+ var tags = nativeSpec[0].split("|");
+ for (var i = 0; i < tags.length; i++) {
+ #interceptorsByTagAccess[tags[i]] = #constructor;
+ #leafTagsAccess[tags[i]] = true;
+ }
+ }
+ if (nativeSpec[1]) {
+ tags = nativeSpec[1].split("|");
+ if (#allowNativesSubclassing) {
+ if (nativeSpec[2]) {
+ var subclasses = nativeSpec[2].split("|");
+ for (var i = 0; i < subclasses.length; i++) {
+ var subclass = #subclassRead;
+ subclass.#nativeSuperclassTagName = tags[0];
+ }
+ }
+ for (i = 0; i < tags.length; i++) {
+ #interceptorsByTagAccess[tags[i]] = #constructor;
+ #leafTagsAccess[tags[i]] = false;
+ }
+ }
+ }
+ }
+ ''', {'info': infoAccess,
+ 'constructor': constructorAccess,
+ 'subclassRead': subclassRead,
+ 'interceptorsByTagAccess': interceptorsByTagAccess,
+ 'leafTagsAccess': leafTagsAccess,
+ 'nativeSuperclassTagName': embeddedNames.NATIVE_SUPERCLASS_TAG_NAME,
+ 'allowNativesSubclassing': true});
+ }
}
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
similarity index 96%
rename from pkg/compiler/lib/src/js_emitter/program_builder.dart
rename to pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index 834fe03..b4a9c37 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -4,18 +4,18 @@
library dart2js.js_emitter.program_builder;
-import 'js_emitter.dart' show computeMixinClass;
-import 'model.dart';
+import '../js_emitter.dart' show computeMixinClass;
+import '../model.dart';
-import '../common.dart';
-import '../js/js.dart' as js;
+import '../../common.dart';
+import '../../js/js.dart' as js;
-import '../js_backend/js_backend.dart' show
+import '../../js_backend/js_backend.dart' show
Namer,
JavaScriptBackend,
JavaScriptConstantCompiler;
-import 'js_emitter.dart' show
+import '../js_emitter.dart' show
ClassStubGenerator,
CodeEmitterTask,
InterceptorStubGenerator,
@@ -24,13 +24,15 @@
RuntimeTypeGenerator,
TypeTestProperties;
-import '../elements/elements.dart' show ParameterElement, MethodElement;
+import '../../elements/elements.dart' show ParameterElement, MethodElement;
-import '../universe/universe.dart' show Universe, TypeMaskSet;
-import '../deferred_load.dart' show DeferredLoadTask, OutputUnit;
+import '../../universe/universe.dart' show Universe, TypeMaskSet;
+import '../../deferred_load.dart' show DeferredLoadTask, OutputUnit;
part 'registry.dart';
+/// Builds a self-contained representation of the program that can then be
+/// emitted more easily by the individual emitters.
class ProgramBuilder {
final Compiler _compiler;
final Namer namer;
@@ -123,12 +125,19 @@
assert(!needsNativeSupport || nativeClasses.isNotEmpty);
+ List<js.TokenFinalizer> finalizers = [_task.metadataCollector];
+ if (backend.namer is js.TokenFinalizer) {
+ var namingFinalizer = backend.namer;
+ finalizers.add(namingFinalizer);
+ }
+
return new Program(
fragments,
holders,
_buildLoadMap(),
_buildTypeToInterceptorMap(),
_task.metadataCollector,
+ finalizers,
needsNativeSupport: needsNativeSupport,
outputContainsConstantList: _task.outputContainsConstantList,
hasIsolateSupport: _compiler.hasIsolateSupport);
@@ -316,7 +325,7 @@
}
Class _buildClass(ClassElement element) {
- bool onlyForRti = _task.typeTestRegistry.rtiNeededClasses.contains(element);
+ bool onlyForRti = _task.classesOnlyNeededForRti.contains(element);
List<Method> methods = [];
List<StubMethod> callStubs = <StubMethod>[];
diff --git a/pkg/compiler/lib/src/js_emitter/registry.dart b/pkg/compiler/lib/src/js_emitter/program_builder/registry.dart
similarity index 100%
rename from pkg/compiler/lib/src/js_emitter/registry.dart
rename to pkg/compiler/lib/src/js_emitter/program_builder/registry.dart
diff --git a/pkg/compiler/lib/src/library_loader.dart b/pkg/compiler/lib/src/library_loader.dart
index 227030a..3e6acb5 100644
--- a/pkg/compiler/lib/src/library_loader.dart
+++ b/pkg/compiler/lib/src/library_loader.dart
@@ -483,7 +483,7 @@
then((Script sourceScript) {
if (sourceScript == null) return;
- CompilationUnitElement unit =
+ CompilationUnitElementX unit =
new CompilationUnitElementX(sourceScript, library);
compiler.withCurrentElement(unit, () {
compiler.scanner.scan(unit);
@@ -524,6 +524,23 @@
});
}
+ /// Loads the deserialized [library] with the [handler].
+ ///
+ /// All libraries imported or exported transitively from [library] will be
+ /// loaded as well.
+ Future<LibraryElement> loadDeserializedLibrary(
+ LibraryDependencyHandler handler,
+ LibraryElement library) async {
+ compiler.onLibraryCreated(library);
+ libraryCanonicalUriMap[library.canonicalUri] = library;
+ await compiler.onLibraryScanned(library, handler);
+ for (LibraryTag tag in library.tags) {
+ LibraryElement dependency = library.getLibraryFromTag(tag);
+ await createLibrary(handler, library, dependency.canonicalUri);
+ }
+ return library;
+ }
+
/**
* Create (or reuse) a library element for the library specified by the
* [resolvedUri].
@@ -540,6 +557,10 @@
if (library != null) {
return new Future.value(library);
}
+ library = compiler.serialization.readLibrary(resolvedUri);
+ if (library != null) {
+ return loadDeserializedLibrary(handler, library);
+ }
var readScript = compiler.readScript;
if (readableUri == null) {
readableUri = resolvedUri;
diff --git a/pkg/compiler/lib/src/mirrors/dart2js_library_mirror.dart b/pkg/compiler/lib/src/mirrors/dart2js_library_mirror.dart
index fd2504a..38925b5 100644
--- a/pkg/compiler/lib/src/mirrors/dart2js_library_mirror.dart
+++ b/pkg/compiler/lib/src/mirrors/dart2js_library_mirror.dart
@@ -33,15 +33,7 @@
* file name (for scripts without a library tag). The latter case is used to
* provide a 'library name' for scripts, to use for instance in dartdoc.
*/
- String get _simpleNameString {
- if (_element.libraryTag != null) {
- return _element.libraryTag.name.toString();
- } else {
- // Use the file name as script name.
- String path = _element.canonicalUri.path;
- return path.substring(path.lastIndexOf('/') + 1);
- }
- }
+ String get _simpleNameString => _element.getLibraryOrScriptName();
Symbol get qualifiedName => simpleName;
diff --git a/pkg/compiler/lib/src/native/ssa.dart b/pkg/compiler/lib/src/native/ssa.dart
index 09f4e03..e622559 100644
--- a/pkg/compiler/lib/src/native/ssa.dart
+++ b/pkg/compiler/lib/src/native/ssa.dart
@@ -92,7 +92,10 @@
js.js.uncachedExpressionTemplate(nativeMethodCall),
backend.dynamicType,
inputs, effects: new SideEffects()));
- builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit);
+ // TODO(johnniwinther): Provide source information.
+ builder
+ .close(new HReturn(builder.pop(), null))
+ .addSuccessor(builder.graph.exit);
} else {
if (parameters.parameterCount != 0) {
compiler.internalError(nativeBody,
diff --git a/pkg/compiler/lib/src/ordered_typeset.dart b/pkg/compiler/lib/src/ordered_typeset.dart
index 1ec9ff3..c3bb16d7 100644
--- a/pkg/compiler/lib/src/ordered_typeset.dart
+++ b/pkg/compiler/lib/src/ordered_typeset.dart
@@ -32,7 +32,7 @@
final Link<DartType> types;
final Link<DartType> _supertypes;
- OrderedTypeSet._internal(List<Link<DartType>> this._levels,
+ OrderedTypeSet.internal(List<Link<DartType>> this._levels,
Link<DartType> this.types,
Link<DartType> this._supertypes);
@@ -41,7 +41,7 @@
new LinkEntry<DartType>(type, const Link<DartType>());
List<Link<DartType>> list = new List<Link<DartType>>(1);
list[0] = types;
- return new OrderedTypeSet._internal(list, types, const Link<DartType>());
+ return new OrderedTypeSet.internal(list, types, const Link<DartType>());
}
/// Creates a new [OrderedTypeSet] for [type] when it directly extends the
@@ -58,7 +58,7 @@
list[i] = _levels[i];
}
list[levels] = extendedTypes;
- return new OrderedTypeSet._internal(
+ return new OrderedTypeSet.internal(
list, extendedTypes, _supertypes.prepend(types.head));
}
@@ -75,6 +75,21 @@
return const Link<DartType>();
}
+ /// Returns the offsets into [types] at which each level begins.
+ List<int> get levelOffsets {
+ List<int> offsets = new List.filled(levels, -1);
+ int offset = 0;
+ Link<DartType> pointer = types;
+ for (int depth = maxDepth; depth >= 0; depth--) {
+ while (!identical(pointer, _levels[depth])) {
+ pointer = pointer.tail;
+ offset++;
+ }
+ offsets[depth] = offset;
+ }
+ return offsets;
+ }
+
void forEach(int level, void f(DartType type)) {
if (level < levels) {
Link<DartType> pointer = _levels[level];
@@ -180,7 +195,7 @@
OrderedTypeSet toTypeSet() {
List<Link<DartType>> levels = new List<Link<DartType>>(maxDepth + 1);
if (maxDepth < 0) {
- return new OrderedTypeSet._internal(
+ return new OrderedTypeSet.internal(
levels, const Link<DartType>(), const Link<DartType>());
}
Link<DartType> next = const Link<DartType>();
@@ -198,7 +213,7 @@
next = first;
}
}
- return new OrderedTypeSet._internal(
+ return new OrderedTypeSet.internal(
levels, levels.last, allSupertypes.toLink());
}
diff --git a/pkg/compiler/lib/src/patch_parser.dart b/pkg/compiler/lib/src/patch_parser.dart
index b0bde83..297897b 100644
--- a/pkg/compiler/lib/src/patch_parser.dart
+++ b/pkg/compiler/lib/src/patch_parser.dart
@@ -436,10 +436,10 @@
if (annotation.beginToken != null) {
if (annotation.beginToken.next.value == 'patch') {
return const PatchVersion(null);
- } else if (annotation.beginToken.next.value == 'patch_old') {
- return const PatchVersion('old');
- } else if (annotation.beginToken.next.value == 'patch_new') {
- return const PatchVersion('new');
+ } else if (annotation.beginToken.next.value == 'patch_full') {
+ return const PatchVersion('full');
+ } else if (annotation.beginToken.next.value == 'patch_lazy') {
+ return const PatchVersion('lazy');
}
}
return null;
diff --git a/pkg/compiler/lib/src/resolution/class_hierarchy.dart b/pkg/compiler/lib/src/resolution/class_hierarchy.dart
index 13a6855..712e78e 100644
--- a/pkg/compiler/lib/src/resolution/class_hierarchy.dart
+++ b/pkg/compiler/lib/src/resolution/class_hierarchy.dart
@@ -270,21 +270,22 @@
new Modifiers.withFlags(new NodeList.empty(), Modifiers.FLAG_ABSTRACT));
// Create synthetic type variables for the mixin application.
List<DartType> typeVariables = <DartType>[];
- element.typeVariables.forEach((TypeVariableType type) {
+ int index = 0;
+ for (TypeVariableType type in element.typeVariables) {
TypeVariableElementX typeVariableElement = new TypeVariableElementX(
- type.name, mixinApplication, type.element.node);
+ type.name, mixinApplication, index, type.element.node);
TypeVariableType typeVariable = new TypeVariableType(typeVariableElement);
typeVariables.add(typeVariable);
- });
+ index++;
+ }
// Setup bounds on the synthetic type variables.
- int index = 0;
- element.typeVariables.forEach((TypeVariableType type) {
- TypeVariableType typeVariable = typeVariables[index++];
+ for (TypeVariableType type in element.typeVariables) {
+ TypeVariableType typeVariable = typeVariables[type.element.index];
TypeVariableElementX typeVariableElement = typeVariable.element;
typeVariableElement.typeCache = typeVariable;
typeVariableElement.boundCache =
type.element.bound.subst(typeVariables, element.typeVariables);
- });
+ }
// Setup this and raw type for the mixin application.
mixinApplication.computeThisAndRawType(compiler, typeVariables);
// Substitute in synthetic type variables in super and mixin types.
@@ -309,8 +310,11 @@
FunctionElement createForwardingConstructor(ConstructorElement target,
ClassElement enclosing) {
- return new SynthesizedConstructorElementX.notForDefault(
- target.name, target, enclosing);
+ FunctionElement constructor =
+ new SynthesizedConstructorElementX.notForDefault(
+ target.name, target, enclosing);
+ constructor.computeType(compiler);
+ return constructor;
}
void doApplyMixinTo(MixinApplicationElementX mixinApplication,
@@ -566,8 +570,10 @@
super(compiler);
void loadSupertype(ClassElement element, Node from) {
- compiler.resolver.loadSupertypes(element, from);
- element.ensureResolved(compiler);
+ if (!element.isResolved) {
+ compiler.resolver.loadSupertypes(element, from);
+ element.ensureResolved(compiler);
+ }
}
void visitNodeList(NodeList node) {
diff --git a/pkg/compiler/lib/src/resolution/constructors.dart b/pkg/compiler/lib/src/resolution/constructors.dart
index bcf6113..f3f6be9 100644
--- a/pkg/compiler/lib/src/resolution/constructors.dart
+++ b/pkg/compiler/lib/src/resolution/constructors.dart
@@ -221,7 +221,7 @@
diagnosticNode, kind, {'constructorName': fullConstructorName});
isValidAsConstant = false;
} else {
- lookedupConstructor.computeSignature(visitor.compiler);
+ lookedupConstructor.computeType(visitor.compiler);
if (!call.signatureApplies(lookedupConstructor.functionSignature)) {
MessageKind kind = isImplicitSuperCall
? MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT
diff --git a/pkg/compiler/lib/src/resolution/enum_creator.dart b/pkg/compiler/lib/src/resolution/enum_creator.dart
index d68e961..7a25972 100644
--- a/pkg/compiler/lib/src/resolution/enum_creator.dart
+++ b/pkg/compiler/lib/src/resolution/enum_creator.dart
@@ -14,11 +14,9 @@
// TODO(johnniwinther): Merge functionality with the `TreePrinter`.
class AstBuilder {
- final Token position;
+ final int charOffset;
- AstBuilder(this.position);
-
- int get charOffset => position.charOffset;
+ AstBuilder(this.charOffset);
Modifiers modifiers({bool isConst: false,
bool isFinal: false,
@@ -43,7 +41,7 @@
}
Token keywordToken(String text) {
- return new KeywordToken(Keyword.keywords[text], position.charOffset);
+ return new KeywordToken(Keyword.keywords[text], charOffset);
}
Token stringToken(String text) {
@@ -183,7 +181,7 @@
void createMembers() {
Enum node = enumClass.node;
InterfaceType enumType = enumClass.thisType;
- AstBuilder builder = new AstBuilder(enumClass.position);
+ AstBuilder builder = new AstBuilder(enumClass.position.charOffset);
InterfaceType intType = compiler.intClass.computeType(compiler);
InterfaceType stringType = compiler.stringClass.computeType(compiler);
@@ -239,7 +237,7 @@
!link.isEmpty;
link = link.tail) {
Identifier name = link.head;
- AstBuilder valueBuilder = new AstBuilder(name.token);
+ AstBuilder valueBuilder = new AstBuilder(name.token.charOffset);
// Add reference for the `values` field.
valueReferences.add(valueBuilder.reference(name));
diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart
index 7c1bdf9..9719345 100644
--- a/pkg/compiler/lib/src/resolution/members.dart
+++ b/pkg/compiler/lib/src/resolution/members.dart
@@ -2068,8 +2068,8 @@
switch (semantics.kind) {
case AccessKind.STATIC_METHOD:
case AccessKind.TOPLEVEL_METHOD:
- MethodElementX method = semantics.element;
- method.computeSignature(compiler);
+ MethodElement method = semantics.element;
+ method.computeType(compiler);
if (!callStructure.signatureApplies(method.functionSignature)) {
registry.registerThrowNoSuchMethod();
registry.registerDynamicInvocation(
diff --git a/pkg/compiler/lib/src/resolution/operators.dart b/pkg/compiler/lib/src/resolution/operators.dart
index 4b9ec51..ac202aa 100644
--- a/pkg/compiler/lib/src/resolution/operators.dart
+++ b/pkg/compiler/lib/src/resolution/operators.dart
@@ -49,6 +49,14 @@
default: return null;
}
}
+
+ static UnaryOperator fromKind(UnaryOperatorKind kind) {
+ switch (kind) {
+ case UnaryOperatorKind.NOT: return NOT;
+ case UnaryOperatorKind.NEGATE: return NEGATE;
+ case UnaryOperatorKind.COMPLEMENT: return COMPLEMENT;
+ }
+ }
}
enum BinaryOperatorKind {
@@ -198,6 +206,32 @@
default: return null;
}
}
+
+ static BinaryOperator fromKind(BinaryOperatorKind kind) {
+ switch (kind) {
+ case BinaryOperatorKind.EQ: return EQ;
+ case BinaryOperatorKind.NOT_EQ: return NOT_EQ;
+ case BinaryOperatorKind.INDEX: return INDEX;
+ case BinaryOperatorKind.MUL: return MUL;
+ case BinaryOperatorKind.DIV: return DIV;
+ case BinaryOperatorKind.MOD: return MOD;
+ case BinaryOperatorKind.IDIV: return IDIV;
+ case BinaryOperatorKind.ADD: return ADD;
+ case BinaryOperatorKind.SUB: return SUB;
+ case BinaryOperatorKind.SHL: return SHL;
+ case BinaryOperatorKind.SHR: return SHR;
+ case BinaryOperatorKind.GTEQ: return GTEQ;
+ case BinaryOperatorKind.GT: return GT;
+ case BinaryOperatorKind.LTEQ: return LTEQ;
+ case BinaryOperatorKind.LT: return LT;
+ case BinaryOperatorKind.AND: return AND;
+ case BinaryOperatorKind.XOR: return XOR;
+ case BinaryOperatorKind.OR: return OR;
+ case BinaryOperatorKind.LOGICAL_AND: return LOGICAL_AND;
+ case BinaryOperatorKind.LOGICAL_OR: return LOGICAL_OR;
+ case BinaryOperatorKind.IF_NULL: return IF_NULL;
+ }
+ }
}
/// The operator !=, which is not user definable operator but instead is a
diff --git a/pkg/compiler/lib/src/serialization/constant_serialization.dart b/pkg/compiler/lib/src/serialization/constant_serialization.dart
new file mode 100644
index 0000000..94dc24f
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/constant_serialization.dart
@@ -0,0 +1,430 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization.constants;
+
+import '../constants/expressions.dart';
+import '../dart_types.dart';
+import '../elements/elements.dart' show FieldElement;
+import '../resolution/operators.dart';
+import '../universe/universe.dart';
+import 'serialization.dart';
+import 'keys.dart';
+
+/// Visitor that serializes a [ConstantExpression] by encoding it into an
+/// [ObjectEncoder].
+///
+/// This class is called from the [Serializer] when a [ConstantExpression] needs
+/// serialization. The [ObjectEncoder] ensures that any [Element], [DartType],
+/// and other [ConstantExpression] that the serialized [ConstantExpression]
+/// depends upon are also serialized.
+class ConstantSerializer
+ extends ConstantExpressionVisitor<dynamic, ObjectEncoder> {
+ const ConstantSerializer();
+
+ @override
+ void visitBinary(BinaryConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setEnum(Key.OPERATOR, exp.operator.kind);
+ encoder.setConstant(Key.LEFT, exp.left);
+ encoder.setConstant(Key.RIGHT, exp.right);
+ }
+
+ @override
+ void visitConcatenate(ConcatenateConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setConstants(Key.ARGUMENTS, exp.expressions);
+ }
+
+ @override
+ void visitConditional(ConditionalConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setConstant(Key.CONDITION, exp.condition);
+ encoder.setConstant(Key.TRUE, exp.trueExp);
+ encoder.setConstant(Key.FALSE, exp.falseExp);
+ }
+
+ @override
+ void visitConstructed(ConstructedConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setElement(Key.ELEMENT, exp.target);
+ encoder.setType(Key.TYPE, exp.type);
+ encoder.setStrings(Key.NAMES, exp.callStructure.namedArguments);
+ encoder.setConstants(Key.ARGUMENTS, exp.arguments);
+ }
+
+ @override
+ void visitFunction(FunctionConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setElement(Key.ELEMENT, exp.element);
+ }
+
+ @override
+ void visitIdentical(IdenticalConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setConstant(Key.LEFT, exp.left);
+ encoder.setConstant(Key.RIGHT, exp.right);
+ }
+
+ @override
+ void visitList(ListConstantExpression exp, ObjectEncoder encoder) {
+ encoder.setType(Key.TYPE, exp.type);
+ encoder.setConstants(Key.VALUES, exp.values);
+ }
+
+ @override
+ void visitMap(MapConstantExpression exp, ObjectEncoder encoder) {
+ encoder.setType(Key.TYPE, exp.type);
+ encoder.setConstants(Key.KEYS, exp.keys);
+ encoder.setConstants(Key.VALUES, exp.values);
+ }
+
+ @override
+ void visitBool(BoolConstantExpression exp, ObjectEncoder encoder) {
+ encoder.setBool(Key.VALUE, exp.primitiveValue);
+ }
+
+ @override
+ void visitInt(IntConstantExpression exp, ObjectEncoder encoder) {
+ encoder.setInt(Key.VALUE, exp.primitiveValue);
+ }
+
+ @override
+ void visitDouble(DoubleConstantExpression exp, ObjectEncoder encoder) {
+ encoder.setDouble(Key.VALUE, exp.primitiveValue);
+ }
+
+ @override
+ void visitString(StringConstantExpression exp, ObjectEncoder encoder) {
+ encoder.setString(Key.VALUE, exp.primitiveValue);
+ }
+
+ @override
+ void visitNull(NullConstantExpression exp, ObjectEncoder encoder) {
+ // No additional data needed.
+ }
+
+ @override
+ void visitSymbol(SymbolConstantExpression exp,
+ ObjectEncoder encoder) {
+ throw new UnsupportedError(
+ "ConstantSerializer.visitSymbol: ${exp.getText()}");
+ }
+
+ @override
+ void visitType(TypeConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setType(Key.TYPE, exp.type);
+ }
+
+ @override
+ void visitUnary(UnaryConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setEnum(Key.OPERATOR, exp.operator.kind);
+ encoder.setConstant(Key.EXPRESSION, exp.expression);
+ }
+
+ @override
+ void visitVariable(VariableConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setElement(Key.ELEMENT, exp.element);
+ }
+
+ @override
+ void visitPositional(PositionalArgumentReference exp,
+ ObjectEncoder encoder) {
+ encoder.setInt(Key.INDEX, exp.index);
+ }
+
+ @override
+ void visitNamed(NamedArgumentReference exp, ObjectEncoder encoder) {
+ encoder.setString(Key.NAME, exp.name);
+ }
+
+ @override
+ void visitBoolFromEnvironment(BoolFromEnvironmentConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setConstant(Key.NAME, exp.name);
+ if (exp.defaultValue != null) {
+ encoder.setConstant(Key.DEFAULT, exp.defaultValue);
+ }
+ }
+
+ @override
+ void visitIntFromEnvironment(IntFromEnvironmentConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setConstant(Key.NAME, exp.name);
+ if (exp.defaultValue != null) {
+ encoder.setConstant(Key.DEFAULT, exp.defaultValue);
+ }
+ }
+
+ @override
+ void visitStringFromEnvironment(StringFromEnvironmentConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setConstant(Key.NAME, exp.name);
+ if (exp.defaultValue != null) {
+ encoder.setConstant(Key.DEFAULT, exp.defaultValue);
+ }
+ }
+
+ @override
+ void visitStringLength(StringLengthConstantExpression exp,
+ ObjectEncoder encoder) {
+ encoder.setConstant(Key.EXPRESSION, exp.expression);
+ }
+
+
+ @override
+ void visitDeferred(DeferredConstantExpression exp,
+ ObjectEncoder encoder) {
+ throw new UnsupportedError(
+ "ConstantSerializer.visitDeferred: ${exp.getText()}");
+ }
+}
+
+/// Utility class for deserializing [ConstantExpression]s.
+///
+/// This is used by the [Deserializer].
+class ConstantDeserializer {
+
+ /// Deserializes a [ConstantExpression] from an [ObjectDecoder].
+ ///
+ /// The class is called from the [Deserializer] when a [ConstantExpression]
+ /// needs deserialization. The [ObjectDecoder] ensures that any [Element],
+ /// [DartType], and other [ConstantExpression] that the deserialized
+ /// [ConstantExpression] depends upon are available.
+ static ConstantExpression deserialize(ObjectDecoder decoder) {
+ ConstantExpressionKind kind =
+ decoder.getEnum(Key.KIND, ConstantExpressionKind.values);
+ switch (kind) {
+ case ConstantExpressionKind.BINARY:
+ BinaryOperator operator = BinaryOperator.fromKind(decoder.getEnum(
+ Key.OPERATOR, BinaryOperatorKind.values));
+ return new BinaryConstantExpression(
+ decoder.getConstant(Key.LEFT),
+ operator,
+ decoder.getConstant(Key.RIGHT));
+ case ConstantExpressionKind.BOOL:
+ return new BoolConstantExpression(
+ decoder.getBool(Key.VALUE));
+ case ConstantExpressionKind.BOOL_FROM_ENVIRONMENT:
+ return new BoolFromEnvironmentConstantExpression(
+ decoder.getConstant(Key.NAME),
+ decoder.getConstant(Key.DEFAULT, isOptional: true));
+ case ConstantExpressionKind.CONCATENATE:
+ return new ConcatenateConstantExpression(
+ decoder.getConstants(Key.ARGUMENTS));
+ case ConstantExpressionKind.CONDITIONAL:
+ return new ConditionalConstantExpression(
+ decoder.getConstant(Key.CONDITION),
+ decoder.getConstant(Key.TRUE),
+ decoder.getConstant(Key.FALSE));
+ case ConstantExpressionKind.CONSTRUCTED:
+ List<String> names =
+ decoder.getStrings(Key.NAMES, isOptional: true);
+ List<ConstantExpression> arguments =
+ decoder.getConstants(Key.ARGUMENTS, isOptional: true);
+ return new ConstructedConstantExpression(
+ decoder.getType(Key.TYPE),
+ decoder.getElement(Key.ELEMENT),
+ new CallStructure(arguments.length, names),
+ arguments);
+ case ConstantExpressionKind.DOUBLE:
+ return new DoubleConstantExpression(decoder.getDouble(Key.VALUE));
+ case ConstantExpressionKind.ERRONEOUS:
+ break;
+ case ConstantExpressionKind.FUNCTION:
+ return new FunctionConstantExpression(
+ decoder.getElement(Key.ELEMENT));
+ case ConstantExpressionKind.IDENTICAL:
+ return new IdenticalConstantExpression(
+ decoder.getConstant(Key.LEFT),
+ decoder.getConstant(Key.RIGHT));
+ case ConstantExpressionKind.INT:
+ return new IntConstantExpression(decoder.getInt(Key.VALUE));
+ case ConstantExpressionKind.INT_FROM_ENVIRONMENT:
+ return new IntFromEnvironmentConstantExpression(
+ decoder.getConstant(Key.NAME),
+ decoder.getConstant(Key.DEFAULT, isOptional: true));
+ case ConstantExpressionKind.LIST:
+ return new ListConstantExpression(
+ decoder.getType(Key.TYPE),
+ decoder.getConstants(Key.VALUES));
+ case ConstantExpressionKind.MAP:
+ return new MapConstantExpression(
+ decoder.getType(Key.TYPE),
+ decoder.getConstants(Key.KEYS),
+ decoder.getConstants(Key.VALUES));
+ case ConstantExpressionKind.NULL:
+ return new NullConstantExpression();
+ case ConstantExpressionKind.STRING:
+ return new StringConstantExpression(
+ decoder.getString(Key.VALUE));
+ case ConstantExpressionKind.STRING_FROM_ENVIRONMENT:
+ return new StringFromEnvironmentConstantExpression(
+ decoder.getConstant(Key.NAME),
+ decoder.getConstant(Key.DEFAULT, isOptional: true));
+ case ConstantExpressionKind.STRING_LENGTH:
+ return new StringLengthConstantExpression(
+ decoder.getConstant(Key.EXPRESSION));
+ case ConstantExpressionKind.SYMBOL:
+ break;
+ case ConstantExpressionKind.TYPE:
+ return new TypeConstantExpression(decoder.getType(Key.TYPE));
+ case ConstantExpressionKind.UNARY:
+ UnaryOperator operator = UnaryOperator.fromKind(
+ decoder.getEnum(Key.OPERATOR, UnaryOperatorKind.values));
+ return new UnaryConstantExpression(
+ operator,
+ decoder.getConstant(Key.EXPRESSION));
+ case ConstantExpressionKind.VARIABLE:
+ return new VariableConstantExpression(
+ decoder.getElement(Key.ELEMENT));
+
+ case ConstantExpressionKind.POSITIONAL_REFERENCE:
+ return new PositionalArgumentReference(
+ decoder.getInt(Key.INDEX));
+ case ConstantExpressionKind.NAMED_REFERENCE:
+ return new NamedArgumentReference(
+ decoder.getString(Key.NAME));
+ case ConstantExpressionKind.DEFERRED:
+ case ConstantExpressionKind.SYNTHETIC:
+ }
+ throw new UnsupportedError(
+ "Unexpected constant kind: ${kind} in $decoder");
+ }
+}
+
+/// Visitor that serializes a [ConstantConstructor] by encoding it into an
+/// [ObjectEncoder].
+///
+/// This class is called from the [ConstructorSerializer] when the [Serializer]
+/// is serializing constant constructor. The [ObjectEncoder] ensures that any
+/// [Element], [DartType], and [ConstantExpression] that the serialized
+/// [ConstantConstructor] depends upon are also serialized.
+class ConstantConstructorSerializer
+ extends ConstantConstructorVisitor<dynamic, ObjectEncoder> {
+ const ConstantConstructorSerializer();
+
+ @override
+ void visit(ConstantConstructor constantConstructor,
+ ObjectEncoder encoder) {
+ encoder.setEnum(Key.KIND, constantConstructor.kind);
+ constantConstructor.accept(this, encoder);
+ }
+
+ @override
+ void visitGenerative(
+ GenerativeConstantConstructor constructor,
+ ObjectEncoder encoder) {
+ encoder.setType(Key.TYPE, constructor.type);
+ MapEncoder defaults = encoder.createMap(Key.DEFAULTS);
+ constructor.defaultValues.forEach((key, e) {
+ defaults.setConstant('$key', e);
+ });
+ ListEncoder fields = encoder.createList(Key.FIELDS);
+ constructor.fieldMap.forEach((FieldElement f, ConstantExpression e) {
+ ObjectEncoder fieldSerializer = fields.createObject();
+ fieldSerializer.setElement(Key.FIELD, f);
+ fieldSerializer.setConstant(Key.CONSTANT, e);
+ });
+ if (constructor.superConstructorInvocation != null) {
+ encoder.setConstant(Key.CONSTRUCTOR,
+ constructor.superConstructorInvocation);
+ }
+ }
+
+ @override
+ void visitRedirectingFactory(
+ RedirectingFactoryConstantConstructor constructor,
+ ObjectEncoder encoder) {
+ encoder.setConstant(Key.CONSTRUCTOR,
+ constructor.targetConstructorInvocation);
+ }
+
+ @override
+ void visitRedirectingGenerative(
+ RedirectingGenerativeConstantConstructor constructor,
+ ObjectEncoder encoder) {
+ MapEncoder defaults = encoder.createMap(Key.DEFAULTS);
+ constructor.defaultValues.forEach((key, ConstantExpression e) {
+ defaults.setConstant('$key', e);
+ });
+ encoder.setConstant(Key.CONSTRUCTOR,
+ constructor.thisConstructorInvocation);
+ }
+}
+/// Utility class for deserializing [ConstantConstructor]s.
+///
+/// This is used by the [ConstructorElementZ].
+class ConstantConstructorDeserializer {
+ /// Deserializes a [ConstantConstructor] from an [ObjectDecoder].
+ ///
+ /// The class is called from the [Deserializer] when a constant constructor
+ /// needs deserialization. The [ObjectDecoder] ensures that any [Element],
+ /// [DartType], and [ConstantExpression] that the deserialized
+ /// [ConstantConstructor] depends upon are available.
+ static ConstantConstructor deserialize(ObjectDecoder decoder) {
+
+ ConstantConstructorKind kind =
+ decoder.getEnum(Key.KIND, ConstantConstructorKind.values);
+
+ DartType readType() {
+ return decoder.getType(Key.TYPE);
+ }
+
+ Map<dynamic/*int|String*/, ConstantExpression> readDefaults() {
+ Map<dynamic, ConstantExpression> defaultValues =
+ <dynamic, ConstantExpression>{};
+ if (decoder.containsKey(Key.DEFAULTS)) {
+ MapDecoder defaultsMap = decoder.getMap(Key.DEFAULTS);
+ defaultsMap.forEachKey((String key) {
+ int index = int.parse(key, onError: (_) => null);
+ if (index != null) {
+ defaultValues[index] = defaultsMap.getConstant(key);
+ } else {
+ defaultValues[key] = defaultsMap.getConstant(key);
+ }
+ });
+ }
+ return defaultValues;
+ }
+
+ Map<FieldElement, ConstantExpression> readFields() {
+ Map<FieldElement, ConstantExpression> fieldMap =
+ <FieldElement, ConstantExpression>{};
+ if (decoder.containsKey(Key.FIELDS)) {
+ ListDecoder fieldsList = decoder.getList(Key.FIELDS);
+ for (int i = 0; i < fieldsList.length; i++) {
+ ObjectDecoder object = fieldsList.getObject(i);
+ FieldElement field = object.getElement(Key.FIELD);
+ ConstantExpression constant = object.getConstant(Key.CONSTANT);
+ fieldMap[field] = constant;
+ }
+ }
+ return fieldMap;
+ }
+
+ ConstructedConstantExpression readConstructorInvocation() {
+ return decoder.getConstant(Key.CONSTRUCTOR, isOptional: true);
+ }
+
+ switch (kind) {
+ case ConstantConstructorKind.GENERATIVE:
+ return new GenerativeConstantConstructor(
+ readType(),
+ readDefaults(),
+ readFields(),
+ readConstructorInvocation());
+ case ConstantConstructorKind.REDIRECTING_GENERATIVE:
+ return new RedirectingGenerativeConstantConstructor(
+ readDefaults(),
+ readConstructorInvocation());
+ case ConstantConstructorKind.REDIRECTING_FACTORY:
+ return new RedirectingFactoryConstantConstructor(
+ readConstructorInvocation());
+ }
+ }
+}
diff --git a/pkg/compiler/lib/src/serialization/element_serialization.dart b/pkg/compiler/lib/src/serialization/element_serialization.dart
new file mode 100644
index 0000000..9ba4db8
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/element_serialization.dart
@@ -0,0 +1,486 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization.elements;
+
+import '../constants/expressions.dart';
+import '../dart_types.dart';
+import '../dart2jslib.dart' show SourceSpan;
+import '../elements/elements.dart';
+import '../tree/tree.dart';
+import 'constant_serialization.dart';
+import 'keys.dart';
+import 'modelz.dart';
+import 'serialization.dart';
+
+/// Enum kinds used for encoding [Element]s.
+enum SerializedElementKind {
+ LIBRARY,
+ COMPILATION_UNIT,
+ CLASS,
+ GENERATIVE_CONSTRUCTOR,
+ FACTORY_CONSTRUCTOR,
+ TOPLEVEL_FIELD,
+ STATIC_FIELD,
+ INSTANCE_FIELD,
+ TOPLEVEL_FUNCTION,
+ TOPLEVEL_GETTER,
+ TOPLEVEL_SETTER,
+ STATIC_FUNCTION,
+ STATIC_GETTER,
+ STATIC_SETTER,
+ INSTANCE_FUNCTION,
+ INSTANCE_GETTER,
+ INSTANCE_SETTER,
+ TYPEDEF,
+ TYPEVARIABLE,
+ PARAMETER,
+ INITIALIZING_FORMAL,
+}
+
+/// Set of serializers used to serialize different kinds of elements by
+/// encoding into them into [ObjectEncoder]s.
+///
+/// This class is called from the [Serializer] when an [Element] needs
+/// serialization. The [ObjectEncoder] ensures that any [Element], [DartType],
+/// and [ConstantExpression] that the serialized [Element] depends upon are also
+/// serialized.
+const List<ElementSerializer> ELEMENT_SERIALIZERS = const [
+ const LibrarySerializer(),
+ const CompilationUnitSerializer(),
+ const ClassSerializer(),
+ const ConstructorSerializer(),
+ const FieldSerializer(),
+ const FunctionSerializer(),
+ const TypedefSerializer(),
+ const TypeVariableSerializer(),
+ const ParameterSerializer(),
+];
+
+/// Interface for a function that can serialize a set of element kinds.
+abstract class ElementSerializer {
+ /// Returns the [SerializedElementKind] for [element] if this serializer
+ /// supports serialization of [element] or `null` otherwise.
+ SerializedElementKind getSerializedKind(Element element);
+
+ /// Serializes [element] into the [encoder] using the [kind] computed
+ /// by [getSerializedKind].
+ void serialize(Element element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind);
+}
+
+class SerializerUtil {
+ /// Serialize the declared members of [element] into [encoder].
+ static void serializeMembers(ScopeContainerElement element,
+ ObjectEncoder encoder) {
+ MapEncoder mapEncoder = encoder.createMap(Key.MEMBERS);
+ element.forEachLocalMember((Element member) {
+ String name = member.name;
+ if (member.isSetter) {
+ name = '$name,=';
+ }
+ mapEncoder.setElement(name, member);
+ });
+ }
+
+ /// Serialize the source position of [element] into [encoder].
+ static void serializePosition(Element element, ObjectEncoder encoder) {
+ if (element.sourcePosition != null) {
+ SourceSpan position = element.sourcePosition;
+ encoder.setInt(Key.OFFSET, position.begin);
+ if (position.uri != element.compilationUnit.script.resourceUri) {
+ // TODO(johnniwinther): What is the base URI in the case?
+ encoder.setUri(Key.URI, element.library.canonicalUri, position.uri);
+ }
+ int length = position.end - position.begin;
+ if (element.name.length != length) {
+ encoder.setInt(Key.LENGTH, length);
+ }
+ }
+ }
+
+ /// Serialize the parameters of [element] into [encoder].
+ static void serializeParameters(FunctionElement element,
+ ObjectEncoder encoder) {
+ FunctionType type = element.type;
+ encoder.setType(Key.RETURN_TYPE, type.returnType);
+ encoder.setElements(Key.PARAMETERS, element.parameters);
+ }
+
+ /// Returns a function that adds the underlying declared elements for a
+ /// particular element into [list].
+ ///
+ /// For instance, for an [AbstractFieldElement] the getter and setter elements
+ /// are added, if available.
+ static flattenElements(List<Element> list) {
+ return (Element element) {
+ // TODO(johnniwinther): Handle ambiguous elements.
+ if (element.isAmbiguous) return;
+ if (element.isAbstractField) {
+ AbstractFieldElement abstractField = element;
+ if (abstractField.getter != null) {
+ list.add(abstractField.getter);
+ }
+ if (abstractField.setter != null) {
+ list.add(abstractField.setter);
+ }
+ } else {
+ list.add(element);
+ }
+ };
+ }
+}
+
+class LibrarySerializer implements ElementSerializer {
+ const LibrarySerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isLibrary) {
+ return SerializedElementKind.LIBRARY;
+ }
+ return null;
+ }
+
+ void serialize(LibraryElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setUri(
+ Key.CANONICAL_URI, element.canonicalUri, element.canonicalUri);
+ encoder.setString(Key.LIBRARY_NAME, element.getLibraryName());
+ SerializerUtil.serializeMembers(element, encoder);
+ encoder.setElement(Key.COMPILATION_UNIT, element.entryCompilationUnit);
+ encoder.setElements(
+ Key.COMPILATION_UNITS, element.compilationUnits.toList());
+ ListEncoder tags = encoder.createList(Key.TAGS);
+
+ for (LibraryTag tag in element.tags) {
+ if (tag is Import) {
+ ObjectEncoder importTag = tags.createObject();
+ importTag.setString(Key.KIND, 'import');
+ importTag.setElement(Key.LIBRARY, element.getLibraryFromTag(tag));
+ } else if (tag is Export) {
+ ObjectEncoder exportTag = tags.createObject();
+ exportTag.setString(Key.KIND, 'export');
+ exportTag.setElement(Key.LIBRARY, element.getLibraryFromTag(tag));
+ }
+ }
+
+ List<Element> imports = <Element>[];
+ element.forEachImport(SerializerUtil.flattenElements(imports));
+ encoder.setElements(Key.IMPORTS, imports);
+
+ List<Element> exports = <Element>[];
+ element.forEachExport(SerializerUtil.flattenElements(exports));
+ encoder.setElements(Key.EXPORTS, exports);
+ }
+}
+
+class CompilationUnitSerializer implements ElementSerializer {
+ const CompilationUnitSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isCompilationUnit) {
+ return SerializedElementKind.COMPILATION_UNIT;
+ }
+ return null;
+ }
+
+ void serialize(CompilationUnitElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setElement(Key.LIBRARY, element.library);
+ encoder.setUri(
+ Key.URI, element.library.canonicalUri, element.script.resourceUri);
+ List<Element> elements = <Element>[];
+ element.forEachLocalMember((e) => elements.add(e));
+ encoder.setElements(Key.ELEMENTS, elements);
+ }
+}
+
+class ClassSerializer implements ElementSerializer {
+ const ClassSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isClass) {
+ return SerializedElementKind.CLASS;
+ }
+ return null;
+ }
+
+ void serialize(ClassElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setElement(Key.LIBRARY, element.library);
+ encoder.setElement(Key.COMPILATION_UNIT, element.compilationUnit);
+ encoder.setString(Key.NAME, element.name);
+ SerializerUtil.serializePosition(element, encoder);
+ encoder.setTypes(Key.TYPE_VARIABLES, element.typeVariables);
+ encoder.setBool(Key.IS_ABSTRACT, element.isAbstract);
+ if (element.supertype != null) {
+ encoder.setType(Key.SUPERTYPE, element.supertype);
+ }
+ // TODO(johnniwinther): Make [OrderedTypeSet] easier to (de)serialize.
+ ObjectEncoder supertypes = encoder.createObject(Key.SUPERTYPES);
+ supertypes.setTypes(Key.TYPES,
+ element.allSupertypesAndSelf.types.toList());
+ supertypes.setTypes(Key.SUPERTYPES,
+ element.allSupertypesAndSelf.supertypes.toList());
+ supertypes.setInts(Key.OFFSETS, element.allSupertypesAndSelf.levelOffsets);
+ encoder.setTypes(Key.INTERFACES, element.interfaces.toList());
+ SerializerUtil.serializeMembers(element, encoder);
+ }
+}
+
+class ConstructorSerializer implements ElementSerializer {
+ const ConstructorSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isGenerativeConstructor) {
+ return SerializedElementKind.GENERATIVE_CONSTRUCTOR;
+ } else if (element.isFactoryConstructor) {
+ return SerializedElementKind.FACTORY_CONSTRUCTOR;
+ }
+ return null;
+ }
+
+ void serialize(ConstructorElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setElement(Key.CLASS, element.enclosingClass);
+ encoder.setType(Key.TYPE, element.type);
+ encoder.setString(Key.NAME, element.name);
+ SerializerUtil.serializePosition(element, encoder);
+ SerializerUtil.serializeParameters(element, encoder);
+ encoder.setBool(Key.IS_CONST, element.isConst);
+ // TODO(johnniwinther): Handle external constructors.
+ encoder.setBool(Key.IS_EXTERNAL, element.isExternal);
+ if (element.isExternal) return;
+ if (element.isConst && !element.isFromEnvironmentConstructor) {
+ ConstantConstructor constantConstructor = element.constantConstructor;
+ ObjectEncoder constantEncoder =
+ encoder.createObject(Key.CONSTRUCTOR);
+ const ConstantConstructorSerializer().visit(
+ constantConstructor, constantEncoder);
+ }
+ }
+}
+
+class FieldSerializer implements ElementSerializer {
+ const FieldSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isField) {
+ if (element.isTopLevel) return SerializedElementKind.TOPLEVEL_FIELD;
+ if (element.isStatic) return SerializedElementKind.STATIC_FIELD;
+ if (element.isInstanceMember) return SerializedElementKind.INSTANCE_FIELD;
+ }
+ return null;
+ }
+
+ void serialize(FieldElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setString(Key.NAME, element.name);
+ SerializerUtil.serializePosition(element, encoder);
+ encoder.setType(Key.TYPE, element.type);
+ encoder.setBool(Key.IS_FINAL, element.isFinal);
+ encoder.setBool(Key.IS_CONST, element.isConst);
+ if (element.isConst) {
+ ConstantExpression constant = element.constant;
+ encoder.setConstant(Key.CONSTANT, constant);
+ }
+ if (kind != SerializedElementKind.TOPLEVEL_FIELD) {
+ encoder.setElement(Key.CLASS, element.enclosingClass);
+ } else {
+ encoder.setElement(Key.LIBRARY, element.library);
+ encoder.setElement(Key.COMPILATION_UNIT, element.compilationUnit);
+ }
+ }
+}
+
+class FunctionSerializer implements ElementSerializer {
+ const FunctionSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isFunction) {
+ if (element.isTopLevel) return SerializedElementKind.TOPLEVEL_FUNCTION;
+ if (element.isStatic) return SerializedElementKind.STATIC_FUNCTION;
+ if (element.isInstanceMember) {
+ return SerializedElementKind.INSTANCE_FUNCTION;
+ }
+ }
+ if (element.isGetter) {
+ if (element.isTopLevel) return SerializedElementKind.TOPLEVEL_GETTER;
+ if (element.isStatic) return SerializedElementKind.STATIC_GETTER;
+ if (element.isInstanceMember) {
+ return SerializedElementKind.INSTANCE_GETTER;
+ }
+ }
+ if (element.isSetter) {
+ if (element.isTopLevel) return SerializedElementKind.TOPLEVEL_SETTER;
+ if (element.isStatic) return SerializedElementKind.STATIC_SETTER;
+ if (element.isInstanceMember) {
+ return SerializedElementKind.INSTANCE_SETTER;
+ }
+ }
+ return null;
+ }
+
+ void serialize(FunctionElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setString(Key.NAME, element.name);
+ SerializerUtil.serializePosition(element, encoder);
+ SerializerUtil.serializeParameters(element, encoder);
+ encoder.setType(Key.TYPE, element.type);
+ if (element.isFunction) {
+ encoder.setBool(Key.IS_OPERATOR, element.isOperator);
+ }
+ if (element.enclosingClass != null) {
+ encoder.setElement(Key.CLASS, element.enclosingClass);
+ } else {
+ encoder.setElement(Key.LIBRARY, element.library);
+ encoder.setElement(Key.COMPILATION_UNIT, element.compilationUnit);
+ }
+ }
+}
+
+class TypedefSerializer implements ElementSerializer {
+ const TypedefSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isTypedef) {
+ return SerializedElementKind.TYPEDEF;
+ }
+ return null;
+ }
+
+ void serialize(TypedefElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setString(Key.NAME, element.name);
+ SerializerUtil.serializePosition(element, encoder);
+ encoder.setType(Key.ALIAS, element.alias);
+ encoder.setElement(Key.LIBRARY, element.library);
+ encoder.setTypes(Key.TYPE_VARIABLES, element.typeVariables);
+ encoder.setElement(Key.COMPILATION_UNIT, element.compilationUnit);
+ }
+}
+
+class TypeVariableSerializer implements ElementSerializer {
+ const TypeVariableSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isTypeVariable) {
+ return SerializedElementKind.TYPEVARIABLE;
+ }
+ return null;
+ }
+
+ void serialize(TypeVariableElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setElement(Key.TYPE_DECLARATION, element.typeDeclaration);
+ encoder.setString(Key.NAME, element.name);
+ SerializerUtil.serializePosition(element, encoder);
+ TypeDeclarationElement typeDeclaration = element.typeDeclaration;
+ encoder.setType(Key.TYPE, element.type);
+ encoder.setInt(Key.INDEX, element.index);
+ encoder.setType(Key.BOUND, element.bound);
+ }
+}
+
+class ParameterSerializer implements ElementSerializer {
+ const ParameterSerializer();
+
+ SerializedElementKind getSerializedKind(Element element) {
+ if (element.isParameter) {
+ return SerializedElementKind.PARAMETER;
+ } else if (element.isInitializingFormal) {
+ return SerializedElementKind.INITIALIZING_FORMAL;
+ }
+ return null;
+ }
+
+ void serialize(ParameterElement element,
+ ObjectEncoder encoder,
+ SerializedElementKind kind) {
+ encoder.setElement(Key.FUNCTION, element.functionDeclaration);
+ encoder.setString(Key.NAME, element.name);
+ SerializerUtil.serializePosition(element, encoder);
+ encoder.setType(Key.TYPE, element.type);
+ encoder.setBool(Key.IS_OPTIONAL, element.isOptional);
+ encoder.setBool(Key.IS_NAMED, element.isNamed);
+ if (element.isOptional) {
+ encoder.setConstant(Key.CONSTANT, element.constant);
+ }
+ if (element.isInitializingFormal) {
+ InitializingFormalElement initializingFormal = element;
+ encoder.setElement(Key.FIELD, initializingFormal.fieldElement);
+ }
+ }
+}
+
+/// Utility class for deserializing [Element]s.
+///
+/// This is used by the [Deserializer].
+class ElementDeserializer {
+
+ /// Deserializes an [Element] from an [ObjectDecoder].
+ ///
+ /// The class is called from the [Deserializer] when an [Element]
+ /// needs deserialization. The [ObjectDecoder] ensures that any [Element],
+ /// [DartType], and [ConstantExpression] that the deserialized [Element]
+ /// depends upon are available.
+ static Element deserialize(ObjectDecoder decoder) {
+ SerializedElementKind elementKind =
+ decoder.getEnum(Key.KIND, SerializedElementKind.values);
+ switch (elementKind) {
+ case SerializedElementKind.LIBRARY:
+ return new LibraryElementZ(decoder);
+ case SerializedElementKind.COMPILATION_UNIT:
+ return new CompilationUnitElementZ(decoder);
+ case SerializedElementKind.CLASS:
+ return new ClassElementZ(decoder);
+ case SerializedElementKind.TOPLEVEL_FIELD:
+ return new TopLevelFieldElementZ(decoder);
+ case SerializedElementKind.STATIC_FIELD:
+ return new StaticFieldElementZ(decoder);
+ case SerializedElementKind.INSTANCE_FIELD:
+ return new InstanceFieldElementZ(decoder);
+ case SerializedElementKind.GENERATIVE_CONSTRUCTOR:
+ return new GenerativeConstructorElementZ(decoder);
+ case SerializedElementKind.FACTORY_CONSTRUCTOR:
+ return new FactoryConstructorElementZ(decoder);
+ case SerializedElementKind.TOPLEVEL_FUNCTION:
+ return new TopLevelFunctionElementZ(decoder);
+ case SerializedElementKind.STATIC_FUNCTION:
+ return new StaticFunctionElementZ(decoder);
+ case SerializedElementKind.INSTANCE_FUNCTION:
+ return new InstanceFunctionElementZ(decoder);
+ case SerializedElementKind.TOPLEVEL_GETTER:
+ return new TopLevelGetterElementZ(decoder);
+ case SerializedElementKind.STATIC_GETTER:
+ return new StaticGetterElementZ(decoder);
+ case SerializedElementKind.INSTANCE_GETTER:
+ return new InstanceGetterElementZ(decoder);
+ case SerializedElementKind.TOPLEVEL_SETTER:
+ return new TopLevelSetterElementZ(decoder);
+ case SerializedElementKind.STATIC_SETTER:
+ return new StaticSetterElementZ(decoder);
+ case SerializedElementKind.INSTANCE_SETTER:
+ return new InstanceSetterElementZ(decoder);
+ case SerializedElementKind.TYPEDEF:
+ return new TypedefElementZ(decoder);
+ case SerializedElementKind.TYPEVARIABLE:
+ return new TypeVariableElementZ(decoder);
+ case SerializedElementKind.PARAMETER:
+ return new ParameterElementZ(decoder);
+ case SerializedElementKind.INITIALIZING_FORMAL:
+ return new InitializingFormalElementZ(decoder);
+ }
+ throw new UnsupportedError("Unexpected element kind '${elementKind}.");
+ }
+}
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/serialization/json_serializer.dart b/pkg/compiler/lib/src/serialization/json_serializer.dart
new file mode 100644
index 0000000..399bdbf
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/json_serializer.dart
@@ -0,0 +1,212 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization.json;
+
+import 'dart:convert';
+import 'keys.dart';
+import 'serialization.dart';
+import 'values.dart';
+
+/// Serialization encoder for JSON.
+class JsonSerializationEncoder implements SerializationEncoder {
+ const JsonSerializationEncoder();
+
+ String encode(ObjectValue objectValue) {
+ return new JsonEncoder.withIndent(' ').convert(
+ const JsonValueEncoder().convert(objectValue));
+ }
+}
+
+/// Serialization decoder for JSON.
+class JsonSerializationDecoder implements SerializationDecoder {
+ const JsonSerializationDecoder();
+
+ Map decode(String text) => JSON.decode(text);
+
+ /// Returns the name of the [key] which used for to store a [Key] into a
+ /// [Map]; corresponding to the encoding of object properties in
+ /// [JsonValueEncoder.visitObject].
+ getObjectPropertyValue(Key key) => key.name;
+}
+
+/// A [ValueVisitor] that computes a JSON object value.
+class JsonValueEncoder implements ValueVisitor {
+ const JsonValueEncoder();
+
+ convert(Value value) => visit(value, null);
+
+ @override
+ visit(Value value, [arg]) => value.accept(this, arg);
+
+ @override
+ bool visitBool(BoolValue value, arg) => value.value;
+
+ @override
+ visitConstant(ConstantValue value, arg) => visit(value.id);
+
+ @override
+ double visitDouble(DoubleValue value, arg) => value.value;
+
+ @override
+ visitElement(ElementValue value, arg) => visit(value.id);
+
+ @override
+ visitEnum(EnumValue value, arg) => value.value.index;
+
+ @override
+ int visitInt(IntValue value, arg) => value.value;
+
+ @override
+ List visitList(ListValue value, arg) => value.values.map(visit).toList();
+
+ @override
+ Map visitMap(MapValue value, arg) {
+ Map<String, dynamic> map = <String, dynamic>{};
+ value.map.forEach((String key, Value value) {
+ map[key] = visit(value);
+ });
+ return map;
+ }
+
+ @override
+ Map visitObject(ObjectValue value, arg) {
+ Map<String, dynamic> map = <String, dynamic>{};
+ value.map.forEach((Key key, Value value) {
+ map[key.name] = visit(value);
+ });
+ return map;
+ }
+
+ @override
+ String visitString(StringValue value, arg) => value.value;
+
+ @override
+ visitType(TypeValue value, arg) => visit(value.id);
+
+ @override
+ visitUri(UriValue value, arg) => '${value.value}';
+}
+
+/// [ValueVisitor] that generates a verbose JSON-like output.
+class PrettyPrintEncoder implements ValueVisitor {
+ StringBuffer buffer;
+
+ String toText(Value value) {
+ buffer = new StringBuffer();
+ visit(value, '');
+ String text = buffer.toString();
+ buffer = null;
+ return text;
+ }
+
+ @override
+ void visit(Value value, String indentation) {
+ value.accept(this, indentation);
+ }
+
+ @override
+ void visitBool(BoolValue value, String indentation) {
+ buffer.write(value.value);
+ }
+
+ @override
+ void visitConstant(ConstantValue value, String indentation) {
+ buffer.write('Constant(${value.id}):${value.constant.getText()}');
+ }
+
+ @override
+ void visitDouble(DoubleValue value, String indentation) {
+ buffer.write(value.value);
+ }
+ @override
+ void visitElement(ElementValue value, String indentation) {
+ buffer.write('Element(${value.id}):${value.element}');
+ }
+
+ @override
+ void visitEnum(EnumValue value, String indentation) {
+ buffer.write(value.value);
+ }
+
+ @override
+ void visitInt(IntValue value, String indentation) {
+ buffer.write(value.value);
+ }
+
+ @override
+ void visitList(ListValue value, String indentation) {
+ if (value.values.isEmpty) {
+ buffer.write('[]');
+ } else {
+ buffer.write('[');
+ bool needsComma = false;
+ String nextIndentation = '${indentation} ';
+ for (Value element in value.values) {
+ if (needsComma) {
+ buffer.write(',');
+ }
+ buffer.write('\n$nextIndentation');
+ visit(element, nextIndentation);
+ needsComma = true;
+ }
+ buffer.write('\n$indentation]');
+ }
+ }
+
+ @override
+ void visitMap(MapValue value, String indentation) {
+ if (value.map.isEmpty) {
+ buffer.write('{}');
+ } else {
+ buffer.write('{');
+ bool needsComma = false;
+ String nextIndentation = '${indentation} ';
+ value.map.forEach((String key, Value subvalue) {
+ if (needsComma) {
+ buffer.write(',');
+ }
+ buffer.write('\n$nextIndentation$key: ');
+ visit(subvalue, nextIndentation);
+ needsComma = true;
+ });
+ buffer.write('\n$indentation}');
+ }
+ }
+
+ @override
+ void visitObject(ObjectValue value, String indentation) {
+ if (value.map.isEmpty) {
+ buffer.write('{}');
+ } else {
+ buffer.write('{');
+ bool needsComma = false;
+ String nextIndentation = '${indentation} ';
+ value.map.forEach((Key key, Value subvalue) {
+ if (needsComma) {
+ buffer.write(',');
+ }
+ buffer.write('\n$nextIndentation$key: ');
+ visit(subvalue, nextIndentation);
+ needsComma = true;
+ });
+ buffer.write('\n$indentation}');
+ }
+ }
+
+ @override
+ void visitString(StringValue value, String indentation) {
+ buffer.write('"${value.value}"');
+ }
+
+ @override
+ void visitType(TypeValue value, String indentation) {
+ buffer.write('Type(${value.id}):${value.type}');
+ }
+
+ @override
+ void visitUri(UriValue value, String indentation) {
+ buffer.write('Uri(${value.value})');
+ }
+}
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/serialization/keys.dart b/pkg/compiler/lib/src/serialization/keys.dart
new file mode 100644
index 0000000..a3c276e
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/keys.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization.keys;
+
+/// Keys used for serialization.
+class Key {
+ static const Key ALIAS = const Key('alias');
+ static const Key ARGUMENTS = const Key('arguments');
+ static const Key BOUND = const Key('bound');
+ static const Key CALL_STRUCTURE = const Key('callStructure');
+ static const Key CANONICAL_URI = const Key('canonicalUri');
+ static const Key CLASS = const Key('class');
+ static const Key COMPILATION_UNIT = const Key('compilation-unit');
+ static const Key COMPILATION_UNITS = const Key('compilation-units');
+ static const Key CONDITION = const Key('condition');
+ static const Key CONSTANT = const Key('constant');
+ static const Key CONSTANTS = const Key('constants');
+ static const Key CONSTRUCTOR = const Key('constructor');
+ static const Key DEFAULT = const Key('default');
+ static const Key DEFAULTS = const Key('defaults');
+ static const Key ELEMENT = const Key('element');
+ static const Key ELEMENTS = const Key('elements');
+ static const Key EXPORTS = const Key('exports');
+ static const Key EXPRESSION = const Key('expression');
+ static const Key FALSE = const Key('false');
+ static const Key FIELD = const Key('field');
+ static const Key FIELDS = const Key('fields');
+ static const Key FUNCTION = const Key('function');
+ static const Key ID = const Key('id');
+ static const Key IMPORTS = const Key('imports');
+ static const Key INTERFACES = const Key('interfaces');
+ static const Key INDEX = const Key('index');
+ static const Key IS_ABSTRACT = const Key('isAbstract');
+ static const Key IS_CONST = const Key('isConst');
+ static const Key IS_EXTERNAL = const Key('isExternal');
+ static const Key IS_FINAL = const Key('isFinal');
+ static const Key IS_NAMED = const Key('isNamed');
+ static const Key IS_OPERATOR = const Key('isOperator');
+ static const Key IS_OPTIONAL = const Key('isOptional');
+ static const Key KEYS = const Key('keys');
+ static const Key KIND = const Key('kind');
+ static const Key LEFT = const Key('left');
+ static const Key LENGTH = const Key('length');
+ static const Key LIBRARY = const Key('library');
+ static const Key LIBRARY_NAME = const Key('library-name');
+ static const Key MEMBERS = const Key('members');
+ static const Key NAME = const Key('name');
+ static const Key NAMES = const Key('names');
+ static const Key NAMED_PARAMETERS = const Key('named-parameters');
+ static const Key NAMED_PARAMETER_TYPES = const Key('named-parameter-types');
+ static const Key OFFSET = const Key('offset');
+ static const Key OFFSETS = const Key('offsets');
+ static const Key OPERATOR = const Key('operator');
+ static const Key OPTIONAL_PARAMETER_TYPES =
+ const Key('optional-parameter-types');
+ static const Key PARAMETERS = const Key('parameters');
+ static const Key PARAMETER_TYPES = const Key('parameter-types');
+ static const Key RETURN_TYPE = const Key('return-type');
+ static const Key RIGHT = const Key('right');
+ static const Key SUPERTYPE = const Key('supertype');
+ static const Key SUPERTYPES = const Key('supertypes');
+ static const Key TAGS = const Key('tags');
+ static const Key TRUE = const Key('true');
+ static const Key TYPE = const Key('type');
+ static const Key TYPES = const Key('types');
+ static const Key TYPE_ARGUMENTS = const Key('type-arguments');
+ static const Key TYPE_DECLARATION = const Key('type-declaration');
+ static const Key TYPE_VARIABLES = const Key('type-variables');
+ static const Key URI = const Key('uri');
+ static const Key VALUE = const Key('value');
+ static const Key VALUES = const Key('values');
+
+ final String name;
+
+ const Key(this.name);
+
+ String toString() => name;
+}
diff --git a/pkg/compiler/lib/src/serialization/modelz.dart b/pkg/compiler/lib/src/serialization/modelz.dart
new file mode 100644
index 0000000..15bfc1e
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/modelz.dart
@@ -0,0 +1,1449 @@
+// Copyright (c) 2015, 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.
+
+/// Implementation of the element model used for deserialiation.
+///
+/// These classes are created by [ElementDeserializer] triggered by the
+/// [Deserializer].
+
+library dart2js.serialization.modelz;
+
+import 'serialization.dart';
+import 'keys.dart';
+import '../constants/expressions.dart';
+import '../dart2jslib.dart'
+ show Backend,
+ Compiler,
+ DiagnosticListener,
+ Script,
+ SourceSpan;
+import '../dart_types.dart';
+import '../elements/elements.dart';
+import '../elements/modelx.dart' show FunctionSignatureX;
+import '../elements/common.dart';
+import '../elements/visitor.dart';
+import '../ordered_typeset.dart';
+import '../resolution/resolution.dart';
+import '../resolution/class_members.dart' as class_members;
+import '../resolution/enum_creator.dart' show AstBuilder;
+import '../scanner/scannerlib.dart' show Token, SEMICOLON_INFO;
+import '../serialization/constant_serialization.dart';
+import '../io/source_file.dart';
+import '../tree/tree.dart';
+import '../util/util.dart' show Link, LinkBuilder;
+
+/// Compute a [Link] from an [Iterable].
+Link toLink(Iterable iterable) {
+ LinkBuilder builder = new LinkBuilder();
+ for (var element in iterable) {
+ builder.addLast(element);
+ }
+ return builder.toLink();
+}
+
+abstract class ElementZ extends Element with ElementCommon {
+ @override
+ bool get isFactoryConstructor => false;
+
+ String toString() {
+ if (enclosingElement == null || isTopLevel) return 'Z$kind($name)';
+ return 'Z$kind(${enclosingElement.name}#$name)';
+ }
+
+ _unsupported(text) => throw new UnsupportedError('${this}.$text');
+
+ @override
+ AnalyzableElement get analyzableElement {
+ Element element = this;
+ if (element is AnalyzableElement) {
+ return element;
+ } else if (enclosingElement != null) {
+ return enclosingElement.analyzableElement;
+ }
+ return null;
+ }
+
+ @override
+ FunctionElement asFunctionElement() => null;
+
+ @override
+ Scope buildScope() => _unsupported('analyzableElement');
+
+ @override
+ CompilationUnitElement get compilationUnit {
+ return _unsupported('compilationUnit');
+ }
+
+ @override
+ ClassElement get contextClass => _unsupported('contextClass');
+
+ @override
+ void diagnose(Element context, DiagnosticListener listener) {
+ _unsupported('diagnose');
+ }
+
+ @override
+ ClassElement get enclosingClass => null;
+
+ @override
+ Element get enclosingClassOrCompilationUnit {
+ return _unsupported('enclosingClassOrCompilationUnit');
+ }
+
+ @override
+ String get fixedBackendName => _unsupported('fixedBackendName');
+
+ @override
+ bool get hasFixedBackendName => _unsupported('hasFixedBackendName');
+
+ @override
+ LibraryElement get implementationLibrary => library;
+
+ @override
+ bool get isAbstract => false;
+
+ @override
+ bool get isAssignable => _unsupported('isAssignable');
+
+ @override
+ bool get isClassMember => false;
+
+ @override
+ bool get isClosure => _unsupported('isClosure');
+
+ @override
+ bool get isConst => _unsupported('isConst');
+
+ @override
+ bool get isDeferredLoaderGetter => false;
+
+ @override
+ bool get isFinal => _unsupported('isFinal');
+
+ @override
+ bool get isInstanceMember => false;
+
+ @override
+ bool get isLocal => false;
+
+ @override
+ bool get isMixinApplication => false;
+
+ @override
+ bool get isNative => false;
+
+ @override
+ bool get isOperator => false;
+
+ @override
+ bool get isStatic => false;
+
+ // TODO(johnniwinther): Find a more precise semantics for this.
+ @override
+ bool get isSynthesized => true;
+
+ @override
+ bool get isTopLevel => false;
+
+ // TODO(johnniwinther): Support metadata.
+ @override
+ Link<MetadataAnnotation> get metadata => const Link<MetadataAnnotation>();
+
+ @override
+ Element get outermostEnclosingMemberOrTopLevel {
+ return _unsupported('outermostEnclosingMemberOrTopLevel');
+ }
+
+ @override
+ Token get position => _unsupported('position');
+}
+
+abstract class DeserializedElementZ extends ElementZ {
+ ObjectDecoder _decoder;
+
+ DeserializedElementZ(this._decoder);
+
+ @override
+ String get name => _decoder.getString(Key.NAME);
+
+ @override
+ SourceSpan get sourcePosition {
+ // TODO(johnniwinther): Should this be cached?
+ int offset = _decoder.getInt(Key.OFFSET, isOptional: true);
+ if (offset == null) return null;
+ Uri uri = _decoder.getUri(Key.URI, isOptional: true);
+ if (uri == null) {
+ uri = compilationUnit.script.readableUri;
+ }
+ int length = _decoder.getInt(Key.LENGTH, isOptional: true);
+ if (length == null) {
+ length = name.length;
+ }
+ return new SourceSpan(uri, offset, offset + length);
+ }
+}
+
+/// Deserializer for a collection of member elements serialized as a map from
+/// names to element declarations.
+///
+/// The serialized data contains the declared getters and setters but lookup
+/// into the map returns an [AbstractFieldElement] for pairs of corresponding
+/// getters and setters.
+///
+/// The underlying map encoding allows for lazy computation of the members upon
+/// query.
+class MappedContainer {
+ Map<String, Element> _lookupCache = {};
+
+ Element lookup(String name, MapDecoder members) {
+ if (_lookupCache.containsKey(name)) {
+ Element element = _lookupCache[name];
+ if (element != null) {
+ return element;
+ }
+ }
+ if (members == null) {
+ return null;
+ }
+ bool hasId = members.containsKey(name);
+ String setterName = '$name,=';
+ bool hasSetterId = members.containsKey(setterName);
+ Element element;
+ Element setterElement;
+ if (!hasId && !hasSetterId) {
+ _lookupCache[name] = null;
+ return null;
+ }
+ bool isAccessor = false;
+ if (hasId) {
+ element = members.getElement(name);
+ isAccessor = element.isGetter;
+ }
+ if (hasSetterId) {
+ setterElement = members.getElement(setterName);
+ isAccessor = true;
+ }
+ if (isAccessor) {
+ element = new AbstractFieldElementZ(name, element, setterElement);
+ }
+ _lookupCache[name] = element;
+ return element;
+ }
+}
+
+/// Deserializer for a collection of member elements serialized as a list of
+/// element declarations.
+///
+/// The serialized data contains the declared getters and setters but lookup
+/// into the map returns an [AbstractFieldElement] for pairs of corresponding
+/// getters and setters.
+///
+/// The underlying list encoding requires the complete lookup map to be computed
+/// before query.
+class ListedContainer {
+ final Map<String, Element> _lookupMap = <String, Element>{};
+
+ ListedContainer(List<Element> elements) {
+ Set<String> accessorNames = new Set<String>();
+ Map<String, Element> getters = <String, Element>{};
+ Map<String, Element> setters = <String, Element>{};
+ for (Element element in elements) {
+ String name = element.name;
+ if (element.isGetter) {
+ accessorNames.add(name);
+ getters[name] = element;
+ // Inserting [element] here to ensure insert order of [name].
+ _lookupMap[name] = element;
+ } else if (element.isSetter) {
+ accessorNames.add(name);
+ setters[name] = element;
+ // Inserting [element] here to ensure insert order of [name].
+ _lookupMap[name] = element;
+ } else {
+ _lookupMap[name] = element;
+ }
+ }
+ for (String name in accessorNames) {
+ _lookupMap[name] =
+ new AbstractFieldElementZ(name, getters[name], setters[name]);
+ }
+ }
+
+ Element lookup(String name) => _lookupMap[name];
+
+ void forEach(f(Element element)) => _lookupMap.values.forEach(f);
+
+ Iterable<Element> get values => _lookupMap.values;
+}
+
+
+abstract class AnalyzableElementMixin implements AnalyzableElement, ElementZ {
+ @override
+ bool get hasTreeElements => _unsupported('hasTreeElements');
+
+ @override
+ TreeElements get treeElements => _unsupported('treeElements');
+}
+
+
+abstract class AstElementMixin implements AstElement, ElementZ {
+ @override
+ bool get hasNode => _unsupported('hasNode');
+
+ @override
+ bool get hasResolvedAst => _unsupported('hasResolvedAst');
+
+ @override
+ get node => _unsupported('node');
+
+ @override
+ ResolvedAst get resolvedAst => _unsupported('resolvedAst');
+}
+
+abstract class ContainerMixin
+ implements DeserializedElementZ, ScopeContainerElement {
+ MappedContainer _membersMap = new MappedContainer();
+
+ @override
+ Element localLookup(String name) {
+ return _membersMap.lookup(
+ name, _decoder.getMap(Key.MEMBERS, isOptional: true));
+ }
+
+ @override
+ void forEachLocalMember(f(Element element)) {
+ MapDecoder members =
+ _decoder.getMap(Key.MEMBERS, isOptional: true);
+ if (members == null) return;
+ members.forEachKey((String key) {
+ Element member = members.getElement(key);
+ if (member != null) {
+ f(member);
+ }
+ });
+ }
+}
+
+class AbstractFieldElementZ extends ElementZ implements AbstractFieldElement {
+ final String name;
+ final FunctionElement getter;
+ final FunctionElement setter;
+
+ AbstractFieldElementZ(this.name, this.getter, this.setter);
+
+ @override
+ ElementKind get kind => ElementKind.ABSTRACT_FIELD;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitAbstractFieldElement(this, arg);
+ }
+
+ @override
+ LibraryElement get library {
+ return getter != null ? getter.library : setter.library;
+ }
+
+ @override
+ Element get enclosingElement {
+ return getter != null ? getter.enclosingElement : setter.enclosingElement;
+ }
+
+ @override
+ SourceSpan get sourcePosition {
+ return getter != null ? getter.sourcePosition : setter.sourcePosition;
+ }
+}
+
+class LibraryElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ ContainerMixin,
+ LibraryElementCommon
+ implements LibraryElement {
+ Uri _canonicalUri;
+ CompilationUnitElement _entryCompilationUnit;
+ Link<CompilationUnitElement> _compilationUnits;
+ Link<Element> _exports;
+ ListedContainer _exportsMap;
+ ListedContainer _importsMap;
+ Map<LibraryTag, LibraryElement> _libraryDependencies;
+
+ LibraryElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.LIBRARY;
+
+ @override
+ Element get enclosingElement => null;
+
+ @override
+ String get name => entryCompilationUnit.name;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitLibraryElement(this, arg);
+ }
+
+ @override
+ LibraryElement get library => this;
+
+ @override
+ Uri get canonicalUri {
+ if (_canonicalUri == null) {
+ _canonicalUri = _decoder.getUri(Key.CANONICAL_URI);
+ }
+ return _canonicalUri;
+ }
+
+ @override
+ CompilationUnitElement get entryCompilationUnit {
+ if (_entryCompilationUnit == null) {
+ _entryCompilationUnit = _decoder.getElement(Key.COMPILATION_UNIT);
+ }
+ return _entryCompilationUnit;
+ }
+
+ @override
+ Link<CompilationUnitElement> get compilationUnits {
+ if (_compilationUnits == null) {
+ _compilationUnits =
+ toLink(_decoder.getElements(Key.COMPILATION_UNITS));
+ }
+ return _compilationUnits;
+ }
+
+ @override
+ bool hasLibraryName() {
+ return getLibraryName() != '';
+ }
+
+ @override
+ String getLibraryName() {
+ return _decoder.getString(Key.LIBRARY_NAME);
+ }
+
+ @override
+ bool get exportsHandled => true;
+
+ void _ensureExports() {
+ if (_exports == null) {
+ _exportsMap = new ListedContainer(_decoder.getElements(Key.EXPORTS));
+ _exports = toLink(_exportsMap.values);
+ }
+ }
+
+ Link<Element> get exports {
+ _ensureExports();
+ return _exports;
+ }
+
+ @override
+ void forEachExport(f(Element element)) {
+ exports.forEach(f);
+ }
+
+ @override
+ Element find(String elementName) {
+ Element element = localLookup(elementName);
+ if (element == null) {
+ _ensureImports();
+ element = _importsMap.lookup(elementName);
+ }
+ return element;
+ }
+
+ @override
+ Element findLocal(String elementName) {
+ return localLookup(elementName);
+ }
+
+ void _ensureLibraryDependencies() {
+ if (_libraryDependencies == null) {
+ _libraryDependencies = <LibraryTag, LibraryElement>{};
+ ListDecoder tags = _decoder.getList(Key.TAGS);
+ AstBuilder builder = new AstBuilder(0);
+ for (int i = 0; i < tags.length; i++) {
+ ObjectDecoder dependency = tags.getObject(i);
+ String kind = dependency.getString(Key.KIND);
+ LibraryElement library = dependency.getElement(Key.LIBRARY);
+ // TODO(johnniwinther): Add `ImportElement` and `ExportElement` to the
+ // element model to avoid hacking up nodes.
+ if (kind == 'import') {
+ Import tag = new Import(
+ builder.keywordToken('import'),
+ builder.literalString(library.canonicalUri.toString())
+ ..getEndToken().next = builder.symbolToken(SEMICOLON_INFO),
+ null, // prefix
+ null, // combinators
+ null, // metadata
+ isDeferred: false);
+ _libraryDependencies[tag] = library;
+ } else if (kind == 'export') {
+ Export tag = new Export(
+ builder.keywordToken('export'),
+ builder.literalString(library.canonicalUri.toString())
+ ..getEndToken().next = builder.symbolToken(SEMICOLON_INFO),
+ null, // combinators
+ null); // metadata
+ _libraryDependencies[tag] = library;
+ }
+ }
+ }
+ }
+
+ @override
+ Iterable<LibraryTag> get tags {
+ _ensureLibraryDependencies();
+ return _libraryDependencies.keys;
+ }
+
+ LibraryElement getLibraryFromTag(LibraryDependency tag) {
+ _ensureLibraryDependencies();
+ return _libraryDependencies[tag];
+ }
+
+ @override
+ bool get canUseNative => false;
+
+ @override
+ Element findExported(String elementName) => _unsupported('findExported');
+
+ void _ensureImports() {
+ if (_importsMap == null) {
+ _importsMap = new ListedContainer(_decoder.getElements(Key.IMPORTS));
+ }
+ }
+
+ @override
+ void forEachImport(f(Element element)) {
+ _ensureImports();
+ _importsMap.forEach(f);
+ }
+
+ @override
+ Link<Import> getImportsFor(Element element) => _unsupported('getImportsFor');
+
+ @override
+ LibraryName get libraryTag => _unsupported('libraryTag');
+
+ String toString() {
+ return 'Zlibrary(${canonicalUri})';
+ }
+}
+
+class ScriptZ implements Script {
+ final Uri resourceUri;
+
+ ScriptZ(this.resourceUri);
+
+ @override
+ Script copyWithFile(SourceFile file) {
+ throw new UnsupportedError('ScriptZ.copyWithFile');
+ }
+
+ @override
+ SourceFile get file => throw new UnsupportedError('ScriptZ.file');
+
+ @override
+ bool get isSynthesized => throw new UnsupportedError('ScriptZ.isSynthesized');
+
+ @override
+ String get name => resourceUri.toString();
+
+ // TODO(johnniwinther): Support the distinction between [readableUri] and
+ // [resourceUri]; needed for platform libraries.
+ @override
+ Uri get readableUri => resourceUri;
+
+ @override
+ String get text => throw new UnsupportedError('ScriptZ.text');
+}
+
+class CompilationUnitElementZ extends DeserializedElementZ
+ with LibraryMemberMixin,
+ CompilationUnitElementCommon
+ implements CompilationUnitElement {
+ List<Element> _members;
+ Script _script;
+
+ CompilationUnitElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.COMPILATION_UNIT;
+
+ @override
+ CompilationUnitElement get compilationUnit => this;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitCompilationUnitElement(this, arg);
+ }
+
+ @override
+ void forEachLocalMember(f(Element element)) {
+ if (_members == null) {
+ _members =
+ _decoder.getElements(Key.ELEMENTS, isOptional: true);
+ }
+ _members.forEach(f);
+ }
+
+ @override
+ Script get script {
+ if (_script == null) {
+ Uri resolvedUri = _decoder.getUri(Key.URI);
+ _script = new ScriptZ(resolvedUri);
+ }
+ return _script;
+ }
+
+ @override
+ String get name => script.name;
+}
+
+
+abstract class LibraryMemberMixin implements DeserializedElementZ {
+ LibraryElement _library;
+ CompilationUnitElement _compilationUnit;
+
+ @override
+ LibraryElement get library {
+ if (_library == null) {
+ _library = _decoder.getElement(Key.LIBRARY);
+ }
+ return _library;
+ }
+
+ @override
+ CompilationUnitElement get compilationUnit {
+ if (_compilationUnit == null) {
+ _compilationUnit = _decoder.getElement(Key.COMPILATION_UNIT);
+ }
+ return _compilationUnit;
+ }
+
+ @override
+ Element get enclosingElement => compilationUnit;
+
+ @override
+ ClassElement get enclosingClass => null;
+
+ @override
+ bool get isTopLevel => true;
+
+ @override
+ bool get isStatic => false;
+}
+
+abstract class ClassMemberMixin implements DeserializedElementZ {
+ ClassElement _class;
+
+ @override
+ Element get enclosingElement => enclosingClass;
+
+ @override
+ ClassElement get enclosingClass {
+ if (_class == null) {
+ _class = _decoder.getElement(Key.CLASS);
+ }
+ return _class;
+ }
+
+ @override
+ bool get isClassMember => true;
+
+ @override
+ LibraryElement get library => enclosingClass.library;
+
+ @override
+ CompilationUnitElement get compilationUnit => enclosingClass.compilationUnit;
+}
+
+abstract class InstanceMemberMixin implements DeserializedElementZ {
+ @override
+ bool get isTopLevel => false;
+
+ @override
+ bool get isStatic => false;
+
+ @override
+ bool get isInstanceMember => true;
+}
+
+abstract class StaticMemberMixin implements DeserializedElementZ {
+ @override
+ bool get isTopLevel => false;
+
+ @override
+ bool get isStatic => true;
+}
+
+abstract class TypedElementMixin
+ implements DeserializedElementZ, TypedElement {
+ DartType _type;
+
+ @override
+ DartType get type {
+ if (_type == null) {
+ _type = _decoder.getType(Key.TYPE);
+ }
+ return _type;
+ }
+
+ @override
+ DartType computeType(Compiler compiler) => type;
+}
+
+abstract class ParametersMixin
+ implements DeserializedElementZ, FunctionTypedElement {
+ FunctionSignature _functionSignature;
+ List<ParameterElement> _parameters;
+
+ bool get hasFunctionSignature => true;
+
+ @override
+ FunctionSignature get functionSignature {
+ if (_functionSignature == null) {
+ List<Element> requiredParameters = [];
+ List<Element> optionalParameters = [];
+ List orderedOptionalParameters = [];
+ int requiredParameterCount = 0;
+ int optionalParameterCount = 0;
+ bool optionalParametersAreNamed = false;
+ List<DartType> parameterTypes = <DartType>[];
+ List<DartType> optionalParameterTypes = <DartType>[];
+ List<String> namedParameters = <String>[];
+ List<DartType> namedParameterTypes = <DartType>[];
+ for (ParameterElement parameter in parameters) {
+ if (parameter.isOptional) {
+ optionalParameterCount++;
+ requiredParameters.add(parameter);
+ orderedOptionalParameters.add(parameter);
+ if (parameter.isNamed) {
+ optionalParametersAreNamed = true;
+ namedParameters.add(parameter.name);
+ namedParameterTypes.add(parameter.type);
+ } else {
+ optionalParameterTypes.add(parameter.type);
+ }
+ } else {
+ requiredParameterCount++;
+ optionalParameters.add(parameter);
+ parameterTypes.add(parameter.type);
+ }
+ }
+ if (optionalParametersAreNamed) {
+ orderedOptionalParameters.sort((Element a, Element b) {
+ return a.name.compareTo(b.name);
+ });
+ }
+
+ FunctionType type = new FunctionType(
+ this,
+ _decoder.getType(Key.RETURN_TYPE),
+ parameterTypes,
+ optionalParameterTypes,
+ namedParameters,
+ namedParameterTypes);
+ _functionSignature = new FunctionSignatureX(
+ requiredParameters: requiredParameters,
+ requiredParameterCount: requiredParameterCount,
+ optionalParameters: optionalParameters,
+ optionalParameterCount: optionalParameterCount,
+ optionalParametersAreNamed: optionalParametersAreNamed,
+ orderedOptionalParameters: orderedOptionalParameters,
+ type: type);
+ }
+ return _functionSignature;
+ }
+
+ List<ParameterElement> get parameters {
+ if (_parameters == null) {
+ _parameters = _decoder.getElements(Key.PARAMETERS, isOptional: true);
+ }
+ return _parameters;
+ }
+}
+
+abstract class FunctionTypedElementMixin
+ implements FunctionElement, DeserializedElementZ {
+ @override
+ AsyncMarker get asyncMarker => _unsupported('');
+
+ @override
+ bool get isExternal => _unsupported('');
+
+ @override
+ FunctionElement asFunctionElement() => this;
+}
+
+class ClassElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ ClassElementCommon,
+ class_members.ClassMemberMixin,
+ ContainerMixin,
+ LibraryMemberMixin,
+ TypeDeclarationMixin<InterfaceType>
+ implements ClassElement {
+ bool _isObject;
+ DartType _supertype;
+ OrderedTypeSet _allSupertypesAndSelf;
+ Link<DartType> _interfaces;
+
+ ClassElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ InterfaceType _createType(List<DartType> typeArguments) {
+ return new InterfaceType(this, typeArguments);
+ }
+
+ @override
+ ElementKind get kind => ElementKind.CLASS;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitClassElement(this, arg);
+ }
+
+ @override
+ DartType get supertype {
+ if (_isObject == null) {
+ _supertype = _decoder.getType(Key.SUPERTYPE, isOptional: true);
+ _isObject = _supertype == null;
+ }
+ return _supertype;
+ }
+
+ @override
+ bool get isAbstract => _decoder.getBool(Key.IS_ABSTRACT);
+
+ @override
+ bool get isObject {
+ return supertype == null;
+ }
+
+ @override
+ void addBackendMember(Element element) => _unsupported('addBackendMember');
+
+ @override
+ OrderedTypeSet get allSupertypesAndSelf {
+ if (_allSupertypesAndSelf == null) {
+ ObjectDecoder supertypesDeserializer =
+ _decoder.getObject(Key.SUPERTYPES);
+ List<int> offsets = supertypesDeserializer.getInts(Key.OFFSETS);
+ List<Link<DartType>> levels = new List<Link<DartType>>(offsets.length);
+ LinkBuilder<DartType> typesBuilder = new LinkBuilder<DartType>();
+ int offset = 0;
+ int depth = offsets.length - 1;
+ for (DartType type in supertypesDeserializer.getTypes(Key.TYPES)) {
+ Link<DartType> link = typesBuilder.addLast(type);
+ if (offsets[depth] == offset) {
+ levels[depth] = link;
+ depth--;
+ }
+ offset++;
+ }
+ LinkBuilder<DartType> supertypesBuilder = new LinkBuilder<DartType>();
+ for (DartType supertype in
+ supertypesDeserializer.getTypes(Key.SUPERTYPES, isOptional: true)) {
+ supertypesBuilder.addLast(supertype);
+ }
+ Link<DartType> types = typesBuilder.toLink();
+ Link<DartType> supertypes = supertypesBuilder.toLink();
+ _allSupertypesAndSelf = new OrderedTypeSet.internal(
+ levels, types, supertypes);
+ }
+ return _allSupertypesAndSelf;
+ }
+
+ @override
+ void forEachBackendMember(void f(Element member)) {
+ _unsupported('forEachBackendMember');
+ }
+
+ @override
+ bool get hasBackendMembers => _unsupported('hasBackendMembers');
+
+ @override
+ bool get hasConstructor => _unsupported('hasConstructor');
+
+ @override
+ bool hasFieldShadowedBy(Element fieldMember) => _unsupported('');
+
+ @override
+ bool get hasIncompleteHierarchy => _unsupported('hasIncompleteHierarchy');
+
+ @override
+ bool get hasLocalScopeMembers => _unsupported('hasLocalScopeMembers');
+
+ @override
+ bool implementsFunction(Compiler compiler) {
+ return _unsupported('implementsFunction');
+ }
+
+ @override
+ Link<DartType> get interfaces {
+ if (_interfaces == null) {
+ _interfaces = toLink(
+ _decoder.getTypes(Key.INTERFACES, isOptional: true));
+ }
+ return _interfaces;
+ }
+
+ @override
+ bool get isEnumClass => false;
+
+ @override
+ bool get isProxy => _unsupported('isProxy');
+
+ @override
+ bool get isUnnamedMixinApplication {
+ return _unsupported('isUnnamedMixinApplication');
+ }
+
+ @override
+ Element lookupBackendMember(String memberName) {
+ return _unsupported('lookupBackendMember');
+ }
+
+ @override
+ ConstructorElement lookupDefaultConstructor() {
+ ConstructorElement constructor = lookupConstructor("");
+ if (constructor != null && constructor.parameters.isEmpty) {
+ return constructor;
+ }
+ return null;
+ }
+
+ @override
+ String get nativeTagInfo => _unsupported('nativeTagInfo');
+
+ @override
+ void reverseBackendMembers() => _unsupported('reverseBackendMembers');
+
+ @override
+ ClassElement get superclass => supertype != null ? supertype.element : null;
+}
+
+abstract class ConstructorElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ ClassMemberMixin,
+ FunctionTypedElementMixin,
+ ParametersMixin,
+ TypedElementMixin,
+ MemberElementMixin
+ implements ConstructorElement {
+ ConstantConstructor _constantConstructor;
+
+ ConstructorElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitConstructorElement(this, arg);
+ }
+
+ @override
+ bool get isConst => _decoder.getBool(Key.IS_CONST);
+
+ @override
+ bool get isExternal => _decoder.getBool(Key.IS_EXTERNAL);
+
+ bool get isFromEnvironmentConstructor {
+ return name == 'fromEnvironment' &&
+ library.isDartCore &&
+ (enclosingClass.name == 'bool' ||
+ enclosingClass.name == 'int' ||
+ enclosingClass.name == 'String');
+ }
+
+ ConstantConstructor get constantConstructor {
+ if (isConst && _constantConstructor == null) {
+ ObjectDecoder data =
+ _decoder.getObject(Key.CONSTRUCTOR, isOptional: true);
+ if (data == null) {
+ assert(isFromEnvironmentConstructor || isExternal);
+ return null;
+ }
+ _constantConstructor = ConstantConstructorDeserializer.deserialize(data);
+ }
+ return _constantConstructor;
+ }
+
+ @override
+ AsyncMarker get asyncMarker => _unsupported('asyncMarker');
+
+ @override
+ InterfaceType computeEffectiveTargetType(InterfaceType newType) {
+ return _unsupported('computeEffectiveTargetType');
+ }
+
+ @override
+ ConstructorElement get definingConstructor {
+ return _unsupported('definingConstructor');
+ }
+
+ @override
+ ConstructorElement get effectiveTarget {
+ return _unsupported('effectiveTarget');
+ }
+
+ @override
+ ConstructorElement get immediateRedirectionTarget {
+ return _unsupported('immediateRedirectionTarget');
+ }
+
+ @override
+ bool get isRedirectingFactory => _unsupported('isRedirectingFactory');
+
+ @override
+ bool get isRedirectingGenerative => _unsupported('isRedirectingGenerative');
+
+ @override
+ bool get isCyclicRedirection => _unsupported('isCyclicRedirection');
+
+ @override
+ PrefixElement get redirectionDeferredPrefix {
+ return _unsupported('redirectionDeferredPrefix');
+ }
+}
+
+class GenerativeConstructorElementZ extends ConstructorElementZ {
+ GenerativeConstructorElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.GENERATIVE_CONSTRUCTOR;
+}
+
+class FactoryConstructorElementZ extends ConstructorElementZ {
+
+ FactoryConstructorElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.FUNCTION;
+
+ @override
+ bool get isFactoryConstructor => true;
+}
+
+abstract class MemberElementMixin
+ implements DeserializedElementZ, MemberElement {
+
+ @override
+ MemberElement get memberContext => this;
+
+ @override
+ Name get memberName => new Name(name, library);
+
+ @override
+ List<FunctionElement> get nestedClosures => const <FunctionElement>[];
+
+}
+
+abstract class FieldElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ TypedElementMixin,
+ MemberElementMixin
+ implements FieldElement {
+ ConstantExpression _constant;
+
+ FieldElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.FIELD;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitFieldElement(this, arg);
+ }
+
+ @override
+ bool get isFinal => _decoder.getBool(Key.IS_FINAL);
+
+ @override
+ bool get isConst => _decoder.getBool(Key.IS_CONST);
+
+ @override
+ ConstantExpression get constant {
+ if (isConst && _constant == null) {
+ _constant = _decoder.getConstant(Key.CONSTANT);
+ }
+ return _constant;
+ }
+
+ @override
+ Expression get initializer => _unsupported('initializer');
+}
+
+
+class TopLevelFieldElementZ extends FieldElementZ with LibraryMemberMixin {
+ TopLevelFieldElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class StaticFieldElementZ extends FieldElementZ
+ with ClassMemberMixin, StaticMemberMixin {
+ StaticFieldElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class InstanceFieldElementZ extends FieldElementZ
+ with ClassMemberMixin, InstanceMemberMixin {
+ InstanceFieldElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+abstract class FunctionElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ ParametersMixin,
+ FunctionTypedElementMixin,
+ TypedElementMixin,
+ MemberElementMixin
+ implements MethodElement {
+ FunctionElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.FUNCTION;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitFunctionElement(this, arg);
+ }
+
+ @override
+ bool get isOperator => _decoder.getBool(Key.IS_OPERATOR);
+}
+
+class TopLevelFunctionElementZ extends FunctionElementZ
+ with LibraryMemberMixin {
+ TopLevelFunctionElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class StaticFunctionElementZ extends FunctionElementZ
+ with ClassMemberMixin, StaticMemberMixin {
+ StaticFunctionElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class InstanceFunctionElementZ extends FunctionElementZ
+ with ClassMemberMixin, InstanceMemberMixin {
+ InstanceFunctionElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+abstract class GetterElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ FunctionTypedElementMixin,
+ ParametersMixin,
+ TypedElementMixin,
+ MemberElementMixin
+ implements FunctionElement {
+
+ GetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.GETTER;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitFunctionElement(this, arg);
+ }
+}
+
+class TopLevelGetterElementZ extends GetterElementZ with LibraryMemberMixin {
+ TopLevelGetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class StaticGetterElementZ extends GetterElementZ
+ with ClassMemberMixin, StaticMemberMixin {
+ StaticGetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class InstanceGetterElementZ extends GetterElementZ
+ with ClassMemberMixin, InstanceMemberMixin {
+ InstanceGetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+abstract class SetterElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ FunctionTypedElementMixin,
+ ParametersMixin,
+ TypedElementMixin,
+ MemberElementMixin
+ implements FunctionElement {
+
+ SetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.SETTER;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitFunctionElement(this, arg);
+ }
+}
+
+class TopLevelSetterElementZ extends SetterElementZ with LibraryMemberMixin {
+ TopLevelSetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class StaticSetterElementZ extends SetterElementZ
+ with ClassMemberMixin, StaticMemberMixin {
+ StaticSetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+class InstanceSetterElementZ extends SetterElementZ
+ with ClassMemberMixin, InstanceMemberMixin {
+ InstanceSetterElementZ(ObjectDecoder decoder)
+ : super(decoder);
+}
+
+abstract class TypeDeclarationMixin<T extends GenericType>
+ implements DeserializedElementZ, TypeDeclarationElement {
+ List<DartType> _typeVariables;
+ T _rawType;
+ T _thisType;
+
+ void _ensureTypes() {
+ if (_typeVariables == null) {
+ _typeVariables = _decoder.getTypes(
+ Key.TYPE_VARIABLES, isOptional: true);
+ _rawType = _createType(new List<DartType>.filled(
+ _typeVariables.length, const DynamicType()));
+ _thisType = _createType(_typeVariables);
+ }
+ }
+
+ T _createType(List<DartType> typeArguments);
+
+ @override
+ List<DartType> get typeVariables {
+ _ensureTypes();
+ return _typeVariables;
+ }
+
+ @override
+ T get rawType {
+ _ensureTypes();
+ return _rawType;
+ }
+
+ @override
+ T get thisType {
+ _ensureTypes();
+ return _thisType;
+ }
+
+ @override
+ T computeType(Compiler compiler) => thisType;
+
+ @override
+ bool get isResolved => true;
+
+ @override
+ void ensureResolved(Compiler compiler) {}
+}
+
+class TypedefElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ LibraryMemberMixin,
+ ParametersMixin,
+ TypeDeclarationMixin<TypedefType>
+ implements TypedefElement {
+ DartType _alias;
+
+ TypedefElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ TypedefType _createType(List<DartType> typeArguments) {
+ return new TypedefType(this, typeArguments);
+ }
+
+ @override
+ ElementKind get kind => ElementKind.TYPEDEF;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitTypedefElement(this, arg);
+ }
+
+ @override
+ DartType get alias {
+ if (_alias == null) {
+ _alias = _decoder.getType(Key.ALIAS);
+ }
+ return _alias;
+ }
+
+ @override
+ void checkCyclicReference(Compiler compiler) {}
+}
+
+class TypeVariableElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ TypedElementMixin
+ implements TypeVariableElement {
+ TypeDeclarationElement _typeDeclaration;
+ TypeVariableType _type;
+ DartType _bound;
+
+ TypeVariableElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ ElementKind get kind => ElementKind.TYPE_VARIABLE;
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitTypeVariableElement(this, arg);
+ }
+
+ @override
+ CompilationUnitElement get compilationUnit {
+ return typeDeclaration.compilationUnit;
+ }
+
+ @override
+ Element get enclosingElement => typeDeclaration;
+
+ @override
+ Element get enclosingClass => typeDeclaration;
+
+ @override
+ int get index => _decoder.getInt(Key.INDEX);
+
+ @override
+ TypeDeclarationElement get typeDeclaration {
+ if (_typeDeclaration == null) {
+ _typeDeclaration =
+ _decoder.getElement(Key.TYPE_DECLARATION);
+ }
+ return _typeDeclaration;
+ }
+
+ DartType get bound {
+ if (_bound == null) {
+ _bound = _decoder.getType(Key.BOUND);
+ }
+ return _bound;
+ }
+
+ @override
+ LibraryElement get library => typeDeclaration.library;
+}
+
+class ParameterElementZ extends DeserializedElementZ
+ with AnalyzableElementMixin,
+ AstElementMixin,
+ TypedElementMixin
+ implements ParameterElement {
+ FunctionElement _functionDeclaration;
+ ConstantExpression _constant;
+ DartType _type;
+
+ ParameterElementZ(ObjectDecoder decoder) : super(decoder);
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitParameterElement(this, arg);
+ }
+
+ @override
+ ConstantExpression get constant {
+ if (isOptional) {
+ if (_constant == null) {
+ _constant = _decoder.getConstant(Key.CONSTANT);
+ }
+ return _constant;
+ }
+ return null;
+ }
+
+ @override
+ CompilationUnitElement get compilationUnit {
+ return functionDeclaration.compilationUnit;
+ }
+
+ @override
+ ExecutableElement get executableContext => functionDeclaration;
+
+ @override
+ Element get enclosingElement => functionDeclaration;
+
+ @override
+ FunctionElement get functionDeclaration {
+ if (_functionDeclaration == null) {
+ _functionDeclaration = _decoder.getElement(Key.FUNCTION);
+ }
+ return _functionDeclaration;
+ }
+
+ @override
+ FunctionSignature get functionSignature => _unsupported('functionSignature');
+
+ @override
+ Expression get initializer => _unsupported('initializer');
+
+ @override
+ bool get isNamed => _decoder.getBool(Key.IS_NAMED);
+
+ @override
+ bool get isOptional => _decoder.getBool(Key.IS_OPTIONAL);
+
+ @override
+ ElementKind get kind => ElementKind.PARAMETER;
+
+ @override
+ LibraryElement get library => executableContext.library;
+
+ @override
+ MemberElement get memberContext => executableContext.memberContext;
+}
+
+
+class InitializingFormalElementZ extends ParameterElementZ
+ implements InitializingFormalElement {
+ FieldElement _fieldElement;
+
+ InitializingFormalElementZ(ObjectDecoder decoder)
+ : super(decoder);
+
+ @override
+ FieldElement get fieldElement {
+ if (_fieldElement == null) {
+ _fieldElement = _decoder.getElement(Key.FIELD);
+ }
+ return _fieldElement;
+ }
+
+ @override
+ accept(ElementVisitor visitor, arg) {
+ return visitor.visitFieldParameterElement(this, arg);
+ }
+
+ @override
+ ElementKind get kind => ElementKind.INITIALIZING_FORMAL;
+
+}
\ No newline at end of file
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
new file mode 100644
index 0000000..5a818ce
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -0,0 +1,864 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization;
+
+import '../elements/elements.dart';
+import '../constants/expressions.dart';
+import '../dart_types.dart';
+
+import 'element_serialization.dart';
+import 'constant_serialization.dart';
+import 'type_serialization.dart';
+import 'keys.dart';
+import 'json_serializer.dart';
+import 'values.dart';
+
+/// An object that supports the encoding an [ObjectValue] for serialization.
+///
+/// The [ObjectEncoder] ensures that nominality and circularities of
+/// non-primitive values like [Element], [DartType] and [ConstantExpression] are
+/// handled.
+class ObjectEncoder extends AbstractEncoder<Key> {
+ /// Creates an [ObjectEncoder] in the scope of [serializer] that uses [map]
+ /// as its internal storage.
+ ObjectEncoder(Serializer serializer, Map<dynamic, Value> map)
+ : super(serializer, map);
+
+ String get _name => 'Object';
+}
+
+/// An object that supports the encoding a [MapValue] for serialization.
+///
+/// The [MapEncoder] ensures that nominality and circularities of
+/// non-primitive values like [Element], [DartType] and [ConstantExpression] are
+/// handled.
+class MapEncoder extends AbstractEncoder<String> {
+ /// Creates an [MapEncoder] in the scope of [serializer] that uses [map]
+ /// as its internal storage.
+ MapEncoder(Serializer serializer, Map<String, Value> map)
+ : super(serializer, map);
+
+ String get _name => 'Map';
+}
+
+/// An object that supports the encoding a [ListValue] containing [ObjectValue]s
+/// or [MapValue]s.
+///
+/// The [ListEncoder] ensures that nominality and circularities of
+/// non-primitive values like [Element], [DartType] and [ConstantExpression] are
+/// handled.
+class ListEncoder {
+ final Serializer _serializer;
+ final List<Value> _list;
+
+ /// Creates an [ListEncoder] in the scope of [_serializer] that uses [_list]
+ /// as its internal storage.
+ ListEncoder(this._serializer, this._list);
+
+ /// Creates an [ObjectEncoder] and adds it to the encoded list.
+ ObjectEncoder createObject() {
+ Map<Key, Value> map = <Key, Value>{};
+ _list.add(new ObjectValue(map));
+ return new ObjectEncoder(_serializer, map);
+ }
+
+ /// Creates an [ObjectEncoder] and adds it to the encoded list.
+ MapEncoder createMap() {
+ Map<String, Value> map = {};
+ _list.add(new MapValue(map));
+ return new MapEncoder(_serializer, map);
+ }
+}
+
+/// Abstract base implementation for [ObjectEncoder] and [MapEncoder].
+abstract class AbstractEncoder<K> {
+ final Serializer _serializer;
+ final Map<K, Value> _map;
+
+ AbstractEncoder(this._serializer, this._map);
+
+ /// The name of the encoder kind. Use for error reporting.
+ String get _name;
+
+ void _checkKey(K key) {
+ if (_map.containsKey(key)) {
+ throw new StateError("$_name value '$key' already in $_map.");
+ }
+ }
+
+ /// Maps the [key] entry to the enum [value] in the encoded object.
+ void setEnum(K key, var value) {
+ _checkKey(key);
+ _map[key] = new EnumValue(value);
+ }
+
+ /// Maps the [key] entry to the [element] in the encoded object.
+ void setElement(K key, Element element) {
+ _checkKey(key);
+ _map[key] = _serializer.createElementValue(element);
+ }
+
+ /// Maps the [key] entry to the [elements] in the encoded object.
+ ///
+ /// If [elements] is empty, it is skipped.
+ void setElements(K key, Iterable<Element> elements) {
+ _checkKey(key);
+ if (elements.isNotEmpty) {
+ _map[key] = new ListValue(
+ elements.map(_serializer.createElementValue).toList());
+ }
+ }
+
+ /// Maps the [key] entry to the [constant] in the encoded object.
+ void setConstant(K key, ConstantExpression constant) {
+ _checkKey(key);
+ _map[key] = _serializer.createConstantValue(constant);
+ }
+
+ /// Maps the [key] entry to the [constants] in the encoded object.
+ ///
+ /// If [constants] is empty, it is skipped.
+ void setConstants(K key, Iterable<ConstantExpression> constants) {
+ _checkKey(key);
+ if (constants.isNotEmpty) {
+ _map[key] = new ListValue(
+ constants.map(_serializer.createConstantValue).toList());
+ }
+ }
+
+ /// Maps the [key] entry to the [type] in the encoded object.
+ void setType(K key, DartType type) {
+ _checkKey(key);
+ _map[key] = _serializer.createTypeValue(type);
+ }
+
+ /// Maps the [key] entry to the [types] in the encoded object.
+ ///
+ /// If [types] is empty, it is skipped.
+ void setTypes(K key, Iterable<DartType> types) {
+ _checkKey(key);
+ if (types.isNotEmpty) {
+ _map[key] =
+ new ListValue(types.map(_serializer.createTypeValue).toList());
+ }
+ }
+
+ /// Maps the [key] entry to the [uri] in the encoded object using [baseUri] to
+ /// relatives the encoding.
+ ///
+ /// For instance, a source file like `sdk/lib/core/string.dart` should be
+ /// serialized relative to the library root.
+ void setUri(K key, Uri baseUri, Uri uri) {
+ _checkKey(key);
+ _map[key] = new UriValue(baseUri, uri);
+ }
+
+ /// Maps the [key] entry to the string [value] in the encoded object.
+ void setString(K key, String value) {
+ _checkKey(key);
+ _map[key] = new StringValue(value);
+ }
+
+ /// Maps the [key] entry to the string [values] in the encoded object.
+ ///
+ /// If [values] is empty, it is skipped.
+ void setStrings(K key, Iterable<String> values) {
+ _checkKey(key);
+ if (values.isNotEmpty) {
+ _map[key] = new ListValue(values.map((v) => new StringValue(v)).toList());
+ }
+ }
+
+ /// Maps the [key] entry to the bool [value] in the encoded object.
+ void setBool(K key, bool value) {
+ _checkKey(key);
+ _map[key] = new BoolValue(value);
+ }
+
+ /// Maps the [key] entry to the int [value] in the encoded object.
+ void setInt(K key, int value) {
+ _checkKey(key);
+ _map[key] = new IntValue(value);
+ }
+
+ /// Maps the [key] entry to the int [values] in this serializer.
+ ///
+ /// If [values] is empty, it is skipped.
+ void setInts(K key, Iterable<int> values) {
+ _checkKey(key);
+ if (values.isNotEmpty) {
+ _map[key] = new ListValue(values.map((v) => new IntValue(v)).toList());
+ }
+ }
+
+ /// Maps the [key] entry to the double [value] in the encoded object.
+ void setDouble(K key, double value) {
+ _checkKey(key);
+ _map[key] = new DoubleValue(value);
+ }
+
+ /// Creates and returns an [ObjectEncoder] that is mapped to the [key]
+ /// entry in the encoded object.
+ ObjectEncoder createObject(K key) {
+ Map<Key, Value> map = <Key, Value>{};
+ _map[key] = new ObjectValue(map);
+ return new ObjectEncoder(_serializer, map);
+ }
+
+ /// Creates and returns a [MapEncoder] that is mapped to the [key] entry
+ /// in the encoded object.
+ MapEncoder createMap(K key) {
+ Map<String, Value> map = <String, Value>{};
+ _map[key] = new MapValue(map);
+ return new MapEncoder(_serializer, map);
+ }
+
+ /// Creates and returns a [ListEncoder] that is mapped to the [key] entry
+ /// in the encoded object.
+ ListEncoder createList(K key) {
+ List<Value> list = <Value>[];
+ _map[key] = new ListValue(list);
+ return new ListEncoder(_serializer, list);
+ }
+
+ String toString() => _map.toString();
+}
+
+/// [ObjectDecoder] reads serialized values from a [Map] encoded from an
+/// [ObjectValue] where properties are stored using [Key] values as keys.
+class ObjectDecoder extends AbstractDecoder<Key> {
+ /// Creates an [ObjectDecoder] that decodes [map] into deserialized values
+ /// using [deserializer] to create canonicalized values.
+ ObjectDecoder(Deserializer deserializer, Map map)
+ : super(deserializer, map);
+
+ @override
+ _getKeyValue(Key key) => _deserializer.decoder.getObjectPropertyValue(key);
+}
+
+/// [MapDecoder] reads serialized values from a [Map] encoded from an
+/// [MapValue] where entries are stored using [String] values as keys.
+class MapDecoder extends AbstractDecoder<String> {
+ /// Creates an [MapDecoder] that decodes [map] into deserialized values
+ /// using [deserializer] to create canonicalized values.
+ MapDecoder(Deserializer deserializer, Map<String, dynamic> map)
+ : super(deserializer, map);
+
+ @override
+ _getKeyValue(String key) => key;
+
+ /// Applies [f] to every key in the decoded [Map].
+ void forEachKey(f(String key)) {
+ _map.keys.forEach(f);
+ }
+}
+
+/// [ListDecoder] reads serialized map or object values from a [List].
+class ListDecoder {
+ final Deserializer _deserializer;
+ final List _list;
+
+ /// Creates a [ListDecoder] that decodes [_list] using [_deserializer] to
+ /// create canonicalized values.
+ ListDecoder(this._deserializer, this._list);
+
+ /// The number of values in the decoded list.
+ int get length => _list.length;
+
+ /// Returns an [ObjectDecoder] for the [index]th object value in the decoded
+ /// list.
+ ObjectDecoder getObject(int index) {
+ return new ObjectDecoder(_deserializer, _list[index]);
+ }
+
+ /// Returns an [MapDecoder] for the [index]th map value in the decoded list.
+ MapDecoder getMap(int index) {
+ return new MapDecoder(_deserializer, _list[index]);
+ }
+}
+
+/// Abstract base implementation for [ObjectDecoder] and [MapDecoder].
+abstract class AbstractDecoder<K> {
+ final Deserializer _deserializer;
+ final Map<K, dynamic> _map;
+
+ AbstractDecoder(this._deserializer, this._map) {
+ assert(_deserializer != null);
+ assert(_map != null);
+ }
+
+ /// Returns the value for [key] defined by the [SerializationDecoder] in used
+ /// [_deserializer].
+ _getKeyValue(K key);
+
+ /// Returns `true` if [key] has an associated value in the decoded object.
+ bool containsKey(K key) => _map.containsKey(_getKeyValue(key));
+
+ /// Returns the enum value from the [enumValues] associated with [key] in the
+ /// decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// [defaultValue] is returned, otherwise an exception is thrown.
+ getEnum(K key, List enumValues, {bool isOptional: false, defaultValue}) {
+ int value = _map[_getKeyValue(key)];
+ if (value == null) {
+ if (isOptional || defaultValue != null) {
+ return defaultValue;
+ }
+ throw new StateError("enum value '$key' not found in $_map.");
+ }
+ return enumValues[value];
+ }
+
+ /// Returns the [Element] value associated with [key] in the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// `null` is returned, otherwise an exception is thrown.
+ Element getElement(K key, {bool isOptional: false}) {
+ int id = _map[_getKeyValue(key)];
+ if (id == null) {
+ if (isOptional) {
+ return null;
+ }
+ throw new StateError("Element value '$key' not found in $_map.");
+ }
+ return _deserializer.deserializeElement(id);
+ }
+
+ /// Returns the list of [Element] values associated with [key] in the decoded
+ /// object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// and empty [List] is returned, otherwise an exception is thrown.
+ List<Element> getElements(K key, {bool isOptional: false}) {
+ List list = _map[_getKeyValue(key)];
+ if (list == null) {
+ if (isOptional) {
+ return const [];
+ }
+ throw new StateError("Elements value '$key' not found in $_map.");
+ }
+ return list.map(_deserializer.deserializeElement).toList();
+ }
+
+ /// Returns the [ConstantExpression] value associated with [key] in the
+ /// decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// `null` is returned, otherwise an exception is thrown.
+ ConstantExpression getConstant(K key, {bool isOptional: false}) {
+ int id = _map[_getKeyValue(key)];
+ if (id == null) {
+ if (isOptional) {
+ return null;
+ }
+ throw new StateError("Constant value '$key' not found in $_map.");
+ }
+ return _deserializer.deserializeConstant(id);
+ }
+
+ /// Returns the list of [ConstantExpression] values associated with [key] in
+ /// the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// and empty [List] is returned, otherwise an exception is thrown.
+ List<ConstantExpression> getConstants(K key, {bool isOptional: false}) {
+ List list = _map[_getKeyValue(key)];
+ if (list == null) {
+ if (isOptional) {
+ return const [];
+ }
+ throw new StateError("Constants value '$key' not found in $_map.");
+ }
+ return list.map(_deserializer.deserializeConstant).toList();
+ }
+
+ /// Returns the [DartType] value associated with [key] in the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// `null` is returned, otherwise an exception is thrown.
+ DartType getType(K key, {bool isOptional: false}) {
+ int id = _map[_getKeyValue(key)];
+ if (id == null) {
+ if (isOptional) {
+ return null;
+ }
+ throw new StateError("Type value '$key' not found in $_map.");
+ }
+ return _deserializer.deserializeType(id);
+ }
+
+ /// Returns the list of [DartType] values associated with [key] in the decoded
+ /// object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// and empty [List] is returned, otherwise an exception is thrown.
+ List<DartType> getTypes(K key, {bool isOptional: false}) {
+ List list = _map[_getKeyValue(key)];
+ if (list == null) {
+ if (isOptional) {
+ return const [];
+ }
+ throw new StateError("Types value '$key' not found in $_map.");
+ }
+ return list.map(_deserializer.deserializeType).toList();
+ }
+
+ /// Returns the [Uri] value associated with [key] in the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// [defaultValue] is returned, otherwise an exception is thrown.
+ Uri getUri(K key, {bool isOptional: false, Uri defaultValue}) {
+ String value = _map[_getKeyValue(key)];
+ if (value == null) {
+ if (isOptional || defaultValue != null) {
+ return defaultValue;
+ }
+ throw new StateError("Uri value '$key' not found in $_map.");
+ }
+ return Uri.parse(value);
+ }
+
+ /// Returns the [String] value associated with [key] in the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// [defaultValue] is returned, otherwise an exception is thrown.
+ String getString(K key, {bool isOptional: false, String defaultValue}) {
+ String value = _map[_getKeyValue(key)];
+ if (value == null) {
+ if (isOptional || defaultValue != null) {
+ return defaultValue;
+ }
+ throw new StateError("String value '$key' not found in $_map.");
+ }
+ return value;
+ }
+
+ /// Returns the list of [String] values associated with [key] in the decoded
+ /// object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// and empty [List] is returned, otherwise an exception is thrown.
+ List<String> getStrings(K key, {bool isOptional: false}) {
+ List list = _map[_getKeyValue(key)];
+ if (list == null) {
+ if (isOptional) {
+ return const [];
+ }
+ throw new StateError("Strings value '$key' not found in $_map.");
+ }
+ return list;
+ }
+
+ /// Returns the [bool] value associated with [key] in the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// [defaultValue] is returned, otherwise an exception is thrown.
+ bool getBool(K key, {bool isOptional: false, bool defaultValue}) {
+ bool value = _map[_getKeyValue(key)];
+ if (value == null) {
+ if (isOptional || defaultValue != null) {
+ return defaultValue;
+ }
+ throw new StateError("bool value '$key' not found in $_map.");
+ }
+ return value;
+ }
+
+ /// Returns the [int] value associated with [key] in the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// [defaultValue] is returned, otherwise an exception is thrown.
+ int getInt(K key, {bool isOptional: false, int defaultValue}) {
+ int value = _map[_getKeyValue(key)];
+ if (value == null) {
+ if (isOptional || defaultValue != null) {
+ return defaultValue;
+ }
+ throw new StateError("int value '$key' not found in $_map.");
+ }
+ return value;
+ }
+
+ /// Returns the list of [int] values associated with [key] in the decoded
+ /// object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// and empty [List] is returned, otherwise an exception is thrown.
+ List<int> getInts(K key, {bool isOptional: false}) {
+ List list = _map[_getKeyValue(key)];
+ if (list == null) {
+ if (isOptional) {
+ return const [];
+ }
+ throw new StateError("Ints value '$key' not found in $_map.");
+ }
+ return list;
+ }
+
+ /// Returns the [double] value associated with [key] in the decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// [defaultValue] is returned, otherwise an exception is thrown.
+ double getDouble(K key, {bool isOptional: false, double defaultValue}) {
+ double value = _map[_getKeyValue(key)];
+ if (value == null) {
+ if (isOptional || defaultValue != null) {
+ return defaultValue;
+ }
+ throw new StateError("double value '$key' not found in $_map.");
+ }
+ return value;
+ }
+
+ /// Returns an [ObjectDecoder] for the map value associated with [key] in the
+ /// decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// `null` is returned, otherwise an exception is thrown.
+ ObjectDecoder getObject(K key, {bool isOptional: false}) {
+ Map map = _map[_getKeyValue(key)];
+ if (map == null) {
+ if (isOptional) {
+ return null;
+ }
+ throw new StateError("Object value '$key' not found in $_map.");
+ }
+ return new ObjectDecoder(_deserializer, map);
+ }
+
+ /// Returns an [MapDecoder] for the map value associated with [key] in the
+ /// decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// `null` is returned, otherwise an exception is thrown.
+ MapDecoder getMap(K key, {bool isOptional: false}) {
+ Map map = _map[_getKeyValue(key)];
+ if (map == null) {
+ if (isOptional) {
+ return null;
+ }
+ throw new StateError("Map value '$key' not found in $_map.");
+ }
+ return new MapDecoder(_deserializer, map);
+ }
+
+ /// Returns an [ListDecoder] for the list value associated with [key] in the
+ /// decoded object.
+ ///
+ /// If no value is associated with [key], then if [isOptional] is `true`,
+ /// `null` is returned, otherwise an exception is thrown.
+ ListDecoder getList(K key, {bool isOptional: false}) {
+ List list = _map[_getKeyValue(key)];
+ if (list == null) {
+ if (isOptional) {
+ return null;
+ }
+ throw new StateError("List value '$key' not found in $_map.");
+ }
+ return new ListDecoder(_deserializer, list);
+ }
+}
+
+/// A nominal object containing its serialized value.
+class DataObject {
+ /// The id for the object.
+ final Value id;
+
+ /// The serialized value of the object.
+ final ObjectValue objectValue;
+
+ DataObject(Value id, EnumValue kind)
+ : this.id = id,
+ this.objectValue =
+ new ObjectValue(<Key, Value>{Key.ID: id, Key.KIND: kind});
+
+ Map<Key, Value> get map => objectValue.map;
+}
+
+/// Serializer for the transitive closure of a collection of libraries.
+///
+/// The serializer creates an [ObjectValue] model of the [Element], [DartType]
+/// and [ConstantExpression] values in the transitive closure of the serialized
+/// libraries.
+///
+/// The model layout of the produced [objectValue] is:
+///
+/// { // Header object
+/// Key.ELEMENTS: [
+/// {...}, // [ObjectValue] of the 0th [Element].
+/// ...
+/// {...}, // [ObjectValue] of the n-th [Element].
+/// ],
+/// Key.TYPES: [
+/// {...}, // [ObjectValue] of the 0th [DartType].
+/// ...
+/// {...}, // [ObjectValue] of the n-th [DartType].
+/// ],
+/// Key.CONSTANTS: [
+/// {...}, // [ObjectValue] of the 0th [ConstantExpression].
+/// ...
+/// {...}, // [ObjectValue] of the n-th [ConstantExpression].
+/// ],
+/// }
+///
+// TODO(johnniwinther): Support per-library serialization and dependencies
+// between serialized subcomponent.
+class Serializer {
+ final SerializationEncoder _encoder;
+
+ Map<Element, DataObject> _elementMap = <Element, DataObject>{};
+ Map<ConstantExpression, DataObject> _constantMap =
+ <ConstantExpression, DataObject>{};
+ Map<DartType, DataObject> _typeMap = <DartType, DataObject>{};
+ List _pendingList = [];
+
+ Serializer(this._encoder);
+
+ /// Add the transitive closure of [library] to this serializer.
+ void serialize(LibraryElement library) {
+ // Call [_getElementDataObject] for its side-effect: To create a
+ // [DataObject] for [library]. If not already created, this will
+ // put the serialization of [library] in the work queue.
+ _getElementDataObject(library);
+ _emptyWorklist();
+ }
+
+ void _emptyWorklist() {
+ while (_pendingList.isNotEmpty) {
+ _pendingList.removeLast()();
+ }
+ }
+
+ /// Returns the [DataObject] for [element].
+ ///
+ /// If [constant] has no [DataObject], a new [DataObject] is created and
+ /// encoding the [ObjectValue] for [constant] is put into the work queue of
+ /// this serializer.
+ DataObject _getElementDataObject(Element element) {
+ if (element == null) {
+ throw new ArgumentError('Serializer._getElementDataObject(null)');
+ }
+ return _elementMap.putIfAbsent(element, () {
+ // Run through [ELEMENT_SERIALIZERS] sequentially to find the one that
+ // deals with [element].
+ for (ElementSerializer serializer in ELEMENT_SERIALIZERS) {
+ SerializedElementKind kind = serializer.getSerializedKind(element);
+ if (kind != null) {
+ DataObject dataObject = new DataObject(
+ new IntValue(_elementMap.length), new EnumValue(kind));
+ // Delay the serialization of the element itself to avoid loops, and
+ // to keep the call stack small.
+ _pendingList.add(() {
+ serializer.serialize(
+ element, new ObjectEncoder(this, dataObject.map), kind);
+ });
+ return dataObject;
+ }
+ }
+ throw new UnsupportedError(
+ 'Unsupported element: $element (${element.kind})');
+ });
+ }
+
+ /// Creates the [ElementValue] for [element].
+ ///
+ /// If [element] has not already been serialized, it is added to the work
+ /// queue of this serializer.
+ ElementValue createElementValue(Element element) {
+ return new ElementValue(element, _getElementDataObject(element).id);
+ }
+
+ /// Returns the [DataObject] for [constant].
+ ///
+ /// If [constant] has no [DataObject], a new [DataObject] is created and
+ /// encoding the [ObjectValue] for [constant] is put into the work queue of
+ /// this serializer.
+ DataObject _getConstantDataObject(ConstantExpression constant) {
+ return _constantMap.putIfAbsent(constant, () {
+ DataObject dataObject = new DataObject(
+ new IntValue(_constantMap.length), new EnumValue(constant.kind));
+ // Delay the serialization of the constant itself to avoid loops, and to
+ // keep the call stack small.
+ _pendingList.add(() => _encodeConstant(constant, dataObject));
+ return dataObject;
+ });
+ }
+
+ /// Encodes [constant] into the [ObjectValue] of [dataObject].
+ void _encodeConstant(ConstantExpression constant, DataObject dataObject) {
+ const ConstantSerializer().visit(constant,
+ new ObjectEncoder(this, dataObject.map));
+ }
+
+ /// Creates the [ConstantValue] for [constant].
+ ///
+ /// If [constant] has not already been serialized, it is added to the work
+ /// queue of this serializer.
+ ConstantValue createConstantValue(ConstantExpression constant) {
+ return new ConstantValue(constant, _getConstantDataObject(constant).id);
+ }
+
+ /// Returns the [DataObject] for [type].
+ ///
+ /// If [type] has no [DataObject], a new [DataObject] is created and
+ /// encoding the [ObjectValue] for [type] is put into the work queue of this
+ /// serializer.
+ DataObject _getTypeDataObject(DartType type) {
+ return _typeMap.putIfAbsent(type, () {
+ DataObject dataObject = new DataObject(
+ new IntValue(_typeMap.length), new EnumValue(type.kind));
+ // Delay the serialization of the type itself to avoid loops, and to keep
+ // the call stack small.
+ _pendingList.add(() => _encodeType(type, dataObject));
+ return dataObject;
+ });
+ }
+
+ /// Encodes [type] into the [ObjectValue] of [dataObject].
+ void _encodeType(DartType type, DataObject dataObject) {
+ const TypeSerializer().visit(type, new ObjectEncoder(this, dataObject.map));
+ }
+
+ /// Creates the [TypeValue] for [type].
+ ///
+ /// If [type] has not already been serialized, it is added to the work
+ /// queue of this serializer.
+ TypeValue createTypeValue(DartType type) {
+ return new TypeValue(type, _getTypeDataObject(type).id);
+ }
+
+ ObjectValue get objectValue {
+ Map<Key, Value> map = <Key, Value>{};
+ map[Key.ELEMENTS] =
+ new ListValue(_elementMap.values.map((l) => l.objectValue).toList());
+ if (_typeMap.isNotEmpty) {
+ map[Key.TYPES] =
+ new ListValue(_typeMap.values.map((l) => l.objectValue).toList());
+ }
+ if (_constantMap.isNotEmpty) {
+ map[Key.CONSTANTS] =
+ new ListValue(_constantMap.values.map((l) => l.objectValue).toList());
+ }
+ return new ObjectValue(map);
+ }
+
+ String toText() {
+ return _encoder.encode(objectValue);
+ }
+
+ String prettyPrint() {
+ PrettyPrintEncoder encoder = new PrettyPrintEncoder();
+ return encoder.toText(objectValue);
+ }
+}
+
+/// Deserializer for a closed collection of libraries.
+// TODO(johnniwinther): Support per-library deserialization and dependencies
+// between deserialized subcomponent.
+class Deserializer {
+ final SerializationDecoder decoder;
+ ObjectDecoder _headerObject;
+ ListDecoder _elementList;
+ ListDecoder _typeList;
+ ListDecoder _constantList;
+ Map<int, Element> _elementMap = {};
+ Map<int, DartType> _typeMap = {};
+ Map<int, ConstantExpression> _constantMap = {};
+
+ Deserializer.fromText(String text, this.decoder) {
+ _headerObject = new ObjectDecoder(this, decoder.decode(text));
+ }
+
+ /// Returns the [ListDecoder] for the [Element]s in this deserializer.
+ ListDecoder get elements {
+ if (_elementList == null) {
+ _elementList = _headerObject.getList(Key.ELEMENTS);
+ }
+ return _elementList;
+ }
+
+ /// Returns the [ListDecoder] for the [DartType]s in this deserializer.
+ ListDecoder get types {
+ if (_typeList == null) {
+ _typeList = _headerObject.getList(Key.TYPES);
+ }
+ return _typeList;
+ }
+
+ /// Returns the [ListDecoder] for the [ConstantExpression]s in this
+ /// deserializer.
+ ListDecoder get constants {
+ if (_constantList == null) {
+ _constantList = _headerObject.getList(Key.CONSTANTS);
+ }
+ return _constantList;
+ }
+
+ /// Returns the [LibraryElement] for [uri] if part of the deserializer.
+ LibraryElement lookupLibrary(Uri uri) {
+ // TODO(johnniwinther): Libraries should be stored explicitly in the header.
+ ListDecoder list = elements;
+ for (int i = 0; i < list.length; i++) {
+ ObjectDecoder object = list.getObject(i);
+ SerializedElementKind kind =
+ object.getEnum(Key.KIND, SerializedElementKind.values);
+ if (kind == SerializedElementKind.LIBRARY) {
+ Uri libraryUri = object.getUri(Key.CANONICAL_URI);
+ if (libraryUri == uri) {
+ return deserializeElement(object.getInt(Key.ID));
+ }
+ }
+ }
+ return null;
+ }
+
+ /// Returns the deserialized [Element] for [id].
+ Element deserializeElement(int id) {
+ if (id == null) throw new ArgumentError('Deserializer.getElement(null)');
+ return _elementMap.putIfAbsent(id, () {
+ return ElementDeserializer.deserialize(elements.getObject(id));
+ });
+ }
+
+ /// Returns the deserialized [DartType] for [id].
+ DartType deserializeType(int id) {
+ if (id == null) throw new ArgumentError('Deserializer.getType(null)');
+ return _typeMap.putIfAbsent(id, () {
+ return TypeDeserializer.deserialize(types.getObject(id));
+ });
+ }
+
+ /// Returns the deserialized [ConstantExpression] for [id].
+ ConstantExpression deserializeConstant(int id) {
+ if (id == null) throw new ArgumentError('Deserializer.getConstant(null)');
+ return _constantMap.putIfAbsent(id, () {
+ return ConstantDeserializer.deserialize(constants.getObject(id));
+ });
+ }
+}
+
+/// Strategy used by [Serializer] to define the memory and output encoding.
+abstract class SerializationEncoder {
+ /// Encode [objectValue] into text.
+ String encode(ObjectValue objectValue);
+}
+
+/// Strategy used by [Deserializer] for decoding and reading data from a
+/// serialized output.
+abstract class SerializationDecoder {
+ /// Decode [text] into [Map] containing the data corresponding to an encoding
+ /// of the serializer header object.
+ Map decode(String text);
+
+ /// Returns the value used to store [key] as a property in the encoding an
+ /// [ObjectValue].
+ ///
+ /// Different encodings have different restrictions and capabilities as how
+ /// to store a [Key] value. For instance: A JSON encoding needs to convert
+ /// [Key] to a [String] to store it in a JSON object; a Dart encoding can
+ /// choose to store a [Key] as an [int] or as the [Key] itself.
+ getObjectPropertyValue(Key key);
+}
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
new file mode 100644
index 0000000..d82bb7e
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization.task;
+
+import '../dart2jslib.dart';
+import '../elements/elements.dart';
+
+/// Task that supports deserialization of elements.
+class SerializationTask extends CompilerTask {
+ SerializationTask(Compiler compiler) : super(compiler);
+
+ DeserializerSystem deserializer;
+
+ String get name => 'Serialization';
+
+ /// Returns the [LibraryElement] for [resolvedUri] if available from
+ /// serialization.
+ LibraryElement readLibrary(Uri resolvedUri) {
+ if (deserializer == null) return null;
+ return deserializer.readLibrary(resolvedUri);
+ }
+
+ /// Returns `true` if [element] has been deserialized.
+ bool isDeserialized(Element element) {
+ return deserializer != null && deserializer.isDeserialized(element);
+ }
+
+ /// Creates the [ResolutionWorkItem] for the deserialized [element].
+ ResolutionWorkItem createResolutionWorkItem(
+ Element element, ItemCompilationContext context) {
+ assert(deserializer != null);
+ assert(isDeserialized(element));
+ return new DeserializedResolutionWorkItem(
+ element, context, deserializer.computeWorldImpact(element));
+ }
+}
+
+/// A [ResolutionWorkItem] for a deserialized element.
+///
+/// This will not resolve the element but only compute the [WorldImpact].
+class DeserializedResolutionWorkItem implements ResolutionWorkItem {
+ final Element element;
+ final ItemCompilationContext compilationContext;
+ final WorldImpact worldImpact;
+ bool _isAnalyzed = false;
+
+ DeserializedResolutionWorkItem(
+ this.element, this.compilationContext, this.worldImpact);
+
+ @override
+ bool get isAnalyzed => _isAnalyzed;
+
+ @override
+ WorldImpact run(Compiler compiler, ResolutionEnqueuer world) {
+ _isAnalyzed = true;
+ world.registerResolvedElement(element);
+ return worldImpact;
+ }
+}
+
+/// The interface for a system that supports deserialization of libraries and
+/// elements.
+abstract class DeserializerSystem {
+ LibraryElement readLibrary(Uri resolvedUri);
+ bool isDeserialized(Element element);
+ WorldImpact computeWorldImpact(Element element);
+}
diff --git a/pkg/compiler/lib/src/serialization/type_serialization.dart b/pkg/compiler/lib/src/serialization/type_serialization.dart
new file mode 100644
index 0000000..7ea60ff
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/type_serialization.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization.types;
+
+import '../dart_types.dart';
+import 'serialization.dart';
+import 'keys.dart';
+
+/// Visitor that serializes a [DartType] by encoding it into an [ObjectEncoder].
+///
+/// This class is called from the [Serializer] when a [DartType] needs
+/// serialization. The [ObjectEncoder] ensures that any [Element], and other
+/// [DartType] that the serialized [DartType] depends upon are also serialized.
+class TypeSerializer extends DartTypeVisitor<dynamic, ObjectEncoder> {
+ const TypeSerializer();
+
+ void visitType(DartType type, ObjectEncoder encoder) {
+ throw new UnsupportedError('Unsupported type: $type');
+ }
+
+ void visitVoidType(VoidType type, ObjectEncoder encoder) {}
+
+ void visitTypeVariableType(TypeVariableType type,
+ ObjectEncoder encoder) {
+ encoder.setElement(Key.ELEMENT, type.element);
+ }
+
+ void visitFunctionType(FunctionType type,
+ ObjectEncoder encoder) {
+ // TODO(johnniwinther): Support encoding of `type.element`.
+ encoder.setType(Key.RETURN_TYPE, type.returnType);
+ encoder.setTypes(Key.PARAMETER_TYPES, type.parameterTypes);
+ encoder.setTypes(
+ Key.OPTIONAL_PARAMETER_TYPES, type.optionalParameterTypes);
+ encoder.setStrings(Key.NAMED_PARAMETERS, type.namedParameters);
+ encoder.setTypes(Key.NAMED_PARAMETER_TYPES, type.namedParameterTypes);
+ }
+
+ void visitMalformedType(MalformedType type, ObjectEncoder encoder) {
+
+ }
+
+ void visitInterfaceType(InterfaceType type, ObjectEncoder encoder) {
+ encoder.setElement(Key.ELEMENT, type.element);
+ encoder.setTypes(Key.TYPE_ARGUMENTS, type.typeArguments);
+ }
+
+ void visitTypedefType(TypedefType type, ObjectEncoder encoder) {
+ encoder.setElement(Key.ELEMENT, type.element);
+ encoder.setTypes(Key.TYPE_ARGUMENTS, type.typeArguments);
+ }
+
+ void visitDynamicType(DynamicType type, ObjectEncoder encoder) {}
+}
+
+
+/// Utility class for deserializing [DartType]s.
+///
+/// This is used by the [Deserializer].
+class TypeDeserializer {
+
+ /// Deserializes a [DartType] from an [ObjectDecoder].
+ ///
+ /// The class is called from the [Deserializer] when a [DartType] needs
+ /// deserialization. The [ObjectDecoder] ensures that any [Element], other
+ /// [DartType] that the deserialized [DartType] depends upon are available.
+ static DartType deserialize(ObjectDecoder decoder) {
+ TypeKind typeKind = decoder.getEnum(Key.KIND, TypeKind.values);
+ switch (typeKind) {
+ case TypeKind.INTERFACE:
+ return new InterfaceType(
+ decoder.getElement(Key.ELEMENT),
+ decoder.getTypes(Key.TYPE_ARGUMENTS, isOptional: true));
+ case TypeKind.FUNCTION:
+ // TODO(johnniwinther): Support decoding of `type.element`.
+ return new FunctionType.synthesized(
+ decoder.getType(Key.RETURN_TYPE),
+ decoder.getTypes(Key.PARAMETER_TYPES, isOptional: true),
+ decoder.getTypes(
+ Key.OPTIONAL_PARAMETER_TYPES, isOptional: true),
+ decoder.getStrings(
+ Key.NAMED_PARAMETERS, isOptional: true),
+ decoder.getTypes(
+ Key.NAMED_PARAMETER_TYPES, isOptional: true));
+ case TypeKind.TYPE_VARIABLE:
+ return new TypeVariableType(
+ decoder.getElement(Key.ELEMENT));
+ case TypeKind.TYPEDEF:
+ return new TypedefType(
+ decoder.getElement(Key.ELEMENT),
+ decoder.getTypes(Key.TYPE_ARGUMENTS, isOptional: true));
+ case TypeKind.STATEMENT:
+ case TypeKind.MALFORMED_TYPE:
+ throw new UnsupportedError("Unexpected type kind '${typeKind}.");
+ case TypeKind.DYNAMIC:
+ return const DynamicType();
+ case TypeKind.VOID:
+ return const VoidType();
+ }
+ }
+}
+
diff --git a/pkg/compiler/lib/src/serialization/values.dart b/pkg/compiler/lib/src/serialization/values.dart
new file mode 100644
index 0000000..4e49de4
--- /dev/null
+++ b/pkg/compiler/lib/src/serialization/values.dart
@@ -0,0 +1,178 @@
+// Copyright (c) 2015, 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.
+
+/// Class hiarchy for semantic wrapping of serializable values.
+
+library dart2js.serialization.values;
+
+import '../constants/expressions.dart';
+import '../dart_types.dart';
+import '../elements/elements.dart';
+import 'keys.dart';
+
+/// Intermediate representation of a serializable value.
+///
+/// Serializable values are
+/// * [bool],
+/// * [int],
+/// * [double],
+/// * [String],
+/// * enum values,
+/// * [ConstantExpression],
+/// * [DartType],
+/// * [Element],
+/// * [Uri],
+/// * lists of serializeable values,
+/// * maps from arbitrary strings to serializable values; these are called
+/// `Map` values, and
+/// * maps from [Key] to serializable values; these are called `Object`
+/// values.
+///
+/// The distinction between map and object values is chosen to provide a more
+/// robust and checkable implementation of the latter; since the keys are drawn
+/// from a fixed typed set of values, consistency between serialization and
+/// deserialization is easierly maintained.
+abstract class Value {
+ accept(ValueVisitor visitor, arg);
+}
+
+class ElementValue implements Value {
+ final Element element;
+ final Value id;
+
+ ElementValue(this.element, this.id);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitElement(this, arg);
+
+ String toString() => element.toString();
+}
+
+class TypeValue implements Value {
+ final DartType type;
+ final Value id;
+
+ TypeValue(this.type, this.id);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitType(this, arg);
+
+ String toString() => type.toString();
+}
+
+class ConstantValue implements Value {
+ final ConstantExpression constant;
+ final Value id;
+
+ ConstantValue(this.constant, this.id);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitConstant(this, arg);
+
+ String toString() => constant.getText();
+}
+
+abstract class PrimitiveValue implements Value {
+ get value;
+
+ String toString() => value.toString();
+}
+
+class BoolValue extends PrimitiveValue {
+ final bool value;
+
+ BoolValue(this.value);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitBool(this, arg);
+}
+
+class IntValue extends PrimitiveValue {
+ final int value;
+
+ IntValue(this.value);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitInt(this, arg);
+}
+
+class DoubleValue extends PrimitiveValue {
+ final double value;
+
+ DoubleValue(this.value);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitDouble(this, arg);
+}
+
+class StringValue extends PrimitiveValue {
+ final String value;
+
+ StringValue(this.value);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitString(this, arg);
+}
+
+class ObjectValue implements Value {
+ final Map<Key, Value> map;
+
+ ObjectValue(this.map);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitObject(this, arg);
+
+ String toString() => map.toString();
+}
+
+class MapValue implements Value {
+ final Map<String, Value> map;
+
+ MapValue(this.map);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitMap(this, arg);
+
+ String toString() => map.toString();
+}
+
+class ListValue implements Value {
+ final List<Value> values;
+
+ ListValue(this.values);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitList(this, arg);
+
+ String toString() => values.toString();
+}
+
+class EnumValue implements Value {
+ final value;
+
+ EnumValue(this.value);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitEnum(this, arg);
+
+ String toString() => value.toString();
+}
+
+class UriValue implements Value {
+ final Uri baseUri;
+ final Uri value;
+
+ UriValue(this.baseUri, this.value);
+
+ accept(ValueVisitor visitor, arg) => visitor.visitUri(this, arg);
+
+ String toString() => value.toString();
+}
+
+/// Visitor for the [Value] class hierarchy.
+abstract class ValueVisitor<R, A> {
+ R visit(Value value, A arg);
+
+ R visitElement(ElementValue value, A arg);
+ R visitType(TypeValue value, A arg);
+ R visitConstant(ConstantValue value, A arg);
+ R visitBool(BoolValue value, A arg);
+ R visitInt(IntValue value, A arg);
+ R visitDouble(DoubleValue value, A arg);
+ R visitString(StringValue value, A arg);
+ R visitObject(ObjectValue value, A arg);
+ R visitMap(MapValue value, A arg);
+ R visitList(ListValue value, A arg);
+ R visitEnum(EnumValue value, A arg);
+ R visitUri(UriValue value, A arg);
+}
diff --git a/pkg/compiler/lib/src/source_file_provider.dart b/pkg/compiler/lib/src/source_file_provider.dart
index b47fc64..cbc6ff3 100644
--- a/pkg/compiler/lib/src/source_file_provider.dart
+++ b/pkg/compiler/lib/src/source_file_provider.dart
@@ -103,6 +103,10 @@
Future/*<List<int> | String>*/ call(Uri resourceUri);
relativizeUri(Uri uri) => relativize(cwd, uri, isWindows);
+
+ SourceFile getSourceFile(Uri resourceUri) {
+ return sourceFiles[resourceUri];
+ }
}
class CompilerSourceFileProvider extends SourceFileProvider {
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 07e7793..f866a0f 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -10,7 +10,7 @@
SsaOptimizerTask optimizer;
SsaFunctionCompiler(JavaScriptBackend backend,
- SourceInformationFactory sourceInformationFactory)
+ SourceInformationStrategy sourceInformationFactory)
: generator = new SsaCodeGeneratorTask(backend, sourceInformationFactory),
builder = new SsaBuilderTask(backend, sourceInformationFactory),
optimizer = new SsaOptimizerTask(backend);
@@ -97,7 +97,7 @@
class SsaBuilderTask extends CompilerTask {
final CodeEmitterTask emitter;
final JavaScriptBackend backend;
- final SourceInformationFactory sourceInformationFactory;
+ final SourceInformationStrategy sourceInformationFactory;
String get name => 'SSA builder';
@@ -480,7 +480,8 @@
* boxed or stored in a closure then the method generates code to retrieve
* the value.
*/
- HInstruction readLocal(Local local) {
+ HInstruction readLocal(Local local,
+ {SourceInformation sourceInformation}) {
if (isAccessedDirectly(local)) {
if (directLocals[local] == null) {
if (local is TypeVariableElement) {
@@ -500,7 +501,7 @@
: builder.getTypeOfCapturedVariable(redirect);
HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
builder.add(fieldGet);
- return fieldGet;
+ return fieldGet..sourceInformation = sourceInformation;
} else if (isBoxed(local)) {
BoxFieldElement redirect = redirectionMapping[local];
// In the function that declares the captured variable the box is
@@ -512,14 +513,14 @@
HInstruction lookup = new HFieldGet(
redirect, box, builder.getTypeOfCapturedVariable(redirect));
builder.add(lookup);
- return lookup;
+ return lookup..sourceInformation = sourceInformation;
} else {
assert(isUsedInTryOrGenerator(local));
HLocalValue localValue = getLocal(local);
HInstruction instruction = new HLocalGet(
local, localValue, builder.backend.dynamicType);
builder.add(instruction);
- return instruction;
+ return instruction..sourceInformation = sourceInformation;
}
}
@@ -531,7 +532,8 @@
return res;
}
- HLocalValue getLocal(Local local) {
+ HLocalValue getLocal(Local local,
+ {SourceInformation sourceInformation}) {
// If the element is a parameter, we already have a
// HParameterValue for it. We cannot create another one because
// it could then have another name than the real parameter. And
@@ -541,7 +543,8 @@
return builder.activationVariables.putIfAbsent(local, () {
JavaScriptBackend backend = builder.backend;
- HLocalValue localValue = new HLocalValue(local, backend.nonNullType);
+ HLocalValue localValue = new HLocalValue(local, backend.nonNullType)
+ ..sourceInformation = sourceInformation;
builder.graph.entry.addAtExit(localValue);
return localValue;
});
@@ -557,7 +560,8 @@
* Sets the [element] to [value]. If the element is boxed or stored in a
* closure then the method generates code to set the value.
*/
- void updateLocal(Local local, HInstruction value) {
+ void updateLocal(Local local, HInstruction value,
+ {SourceInformation sourceInformation}) {
assert(!isStoredInClosureField(local));
if (isAccessedDirectly(local)) {
directLocals[local] = value;
@@ -568,11 +572,13 @@
// Inside the closure the box is stored in a closure-field and cannot
// be accessed directly.
HInstruction box = readLocal(redirect.box);
- builder.add(new HFieldSet(redirect, box, value));
+ builder.add(new HFieldSet(redirect, box, value)
+ ..sourceInformation = sourceInformation);
} else {
assert(isUsedInTryOrGenerator(local));
HLocalValue localValue = getLocal(local);
- builder.add(new HLocalSet(local, localValue, value));
+ builder.add(new HLocalSet(local, localValue, value)
+ ..sourceInformation = sourceInformation);
}
}
@@ -1119,7 +1125,7 @@
SsaBuilder(JavaScriptBackend backend,
CodegenWorkItem work,
this.nativeEmitter,
- SourceInformationFactory sourceInformationFactory)
+ SourceInformationStrategy sourceInformationFactory)
: this.compiler = backend.compiler,
this.backend = backend,
this.constantSystem = backend.constantSystem,
@@ -1129,7 +1135,8 @@
localsHandler = new LocalsHandler(this, work.element, null);
sourceElementStack.add(work.element);
sourceInformationBuilder =
- sourceInformationFactory.forContext(work.element.implementation);
+ sourceInformationFactory.createBuilderForContext(
+ work.element.implementation);
}
@override
@@ -1635,17 +1642,19 @@
if (!backend.operatorEqHandlesNullArgument(functionElement)) {
handleIf(
function,
- () {
+ visitCondition: () {
HParameterValue parameter = parameters.values.first;
push(new HIdentity(
parameter, graph.addConstantNull(compiler), null,
backend.boolType));
},
- () {
+ visitThen: () {
+ // TODO(johnniwinther): Add source information.
closeAndGotoExit(new HReturn(
- graph.addConstantBool(false, compiler)));
+ graph.addConstantBool(false, compiler),
+ null));
},
- null);
+ visitElse: null);
}
}
function.body.accept(this);
@@ -1669,14 +1678,16 @@
HGraph buildLazyInitializer(VariableElement variable) {
inLazyInitializerExpression = true;
- ast.Node node = variable.node;
+ ast.VariableDefinitions node = variable.node;
openFunction(variable, node);
assert(invariant(variable, variable.initializer != null,
message: "Non-constant variable $variable has no initializer."));
visit(variable.initializer);
HInstruction value = pop();
value = potentiallyCheckOrTrustType(value, variable.type);
- closeAndGotoExit(new HReturn(value));
+ ast.SendSet sendSet = node.definitions.nodes.head;
+ closeAndGotoExit(new HReturn(value,
+ sourceInformationBuilder.buildReturn(sendSet.assignmentOperator)));
return closeFunction();
}
@@ -2179,6 +2190,10 @@
ssaType,
constructorArguments,
instantiatedTypes);
+ if (function != null) {
+ newObject.sourceInformation =
+ sourceInformationBuilder.buildGeneric(function);
+ }
add(newObject);
} else {
// Bulk assign to the initialized fields.
@@ -2332,7 +2347,8 @@
}
}
if (inliningStack.isEmpty) {
- closeAndGotoExit(new HReturn(newObject));
+ closeAndGotoExit(new HReturn(newObject,
+ sourceInformationBuilder.buildImplicitReturn(functionElement)));
return closeFunction();
} else {
localsHandler.updateLocal(returnLocal, newObject);
@@ -2634,7 +2650,8 @@
if (throwExpression != null && inliningStack.isEmpty) {
visitThrowExpression(throwExpression.expression);
handleInTryStatement();
- closeAndGotoExit(new HThrow(pop()));
+ closeAndGotoExit(
+ new HThrow(pop(), sourceInformationBuilder.buildThrow(node)));
} else {
visit(node.expression);
pop();
@@ -3129,7 +3146,8 @@
TypeMask type =
new TypeMask.nonNullExact(compiler.functionClass, compiler.world);
- push(new HForeignNew(closureClassElement, type, capturedVariables));
+ push(new HForeignNew(closureClassElement, type, capturedVariables)
+ ..sourceInformation = sourceInformationBuilder.buildGeneric(node));
Element methodElement = nestedClosureData.closureElement;
registry.registerInstantiatedClosure(methodElement);
@@ -3159,16 +3177,23 @@
visitIf(ast.If node) {
assert(isReachable);
- handleIf(node,
- () => visit(node.condition),
- () => visit(node.thenPart),
- node.elsePart != null ? () => visit(node.elsePart) : null);
+ handleIf(
+ node,
+ visitCondition: () => visit(node.condition),
+ visitThen: () => visit(node.thenPart),
+ visitElse: node.elsePart != null ? () => visit(node.elsePart) : null,
+ sourceInformation: sourceInformationBuilder.buildIf(node));
}
void handleIf(ast.Node diagnosticNode,
- void visitCondition(), void visitThen(), void visitElse()) {
+ {void visitCondition(),
+ void visitThen(),
+ void visitElse(),
+ SourceInformation sourceInformation}) {
SsaBranchBuilder branchBuilder = new SsaBranchBuilder(this, diagnosticNode);
- branchBuilder.handleIf(visitCondition, visitThen, visitElse);
+ branchBuilder.handleIf(
+ visitCondition, visitThen, visitElse,
+ sourceInformation: sourceInformation);
}
@override
@@ -3199,8 +3224,10 @@
void visitNot(ast.Send node, ast.Node expression, _) {
assert(node.argumentsNode is ast.Prefix);
visit(expression);
- HNot not = new HNot(popBoolified(), backend.boolType);
- pushWithPosition(not, node);
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGeneric(node);
+ push(new HNot(popBoolified(), backend.boolType)
+ ..sourceInformation = sourceInformation);
}
@override
@@ -3225,7 +3252,8 @@
node,
elements.getSelector(node),
elements.getTypeMask(node),
- [operand]);
+ [operand],
+ sourceInformation: sourceInformationBuilder.buildGeneric(node));
}
@override
@@ -3259,7 +3287,8 @@
elements.getSelector(node),
elements.getTypeMask(node),
node,
- location: node.selector);
+ sourceInformation:
+ sourceInformationBuilder.buildGeneric(node.selector));
}
/// TODO(johnniwinther): Merge [visitBinarySend] with [handleBinary] and
@@ -3269,8 +3298,9 @@
Selector selector,
TypeMask mask,
ast.Send send,
- {ast.Node location}) {
- pushInvokeDynamic(send, selector, mask, [left, right], location: location);
+ {SourceInformation sourceInformation}) {
+ pushInvokeDynamic(send, selector, mask, [left, right],
+ sourceInformation: sourceInformation);
}
HInstruction generateInstanceSendReceiver(ast.Send send) {
@@ -3299,7 +3329,8 @@
HInstruction receiver) {
assert(Elements.isInstanceSend(send, elements));
assert(selector.isGetter);
- pushInvokeDynamic(send, selector, mask, [receiver]);
+ pushInvokeDynamic(send, selector, mask, [receiver],
+ sourceInformation: sourceInformationBuilder.buildGet(send));
}
/// Inserts a call to checkDeferredIsLoaded for [prefixElement].
@@ -3326,15 +3357,20 @@
}
void handleInvalidStaticGet(ast.Send node, Element element) {
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGet(node);
generateThrowNoSuchMethod(
node,
noSuchMethodTargetSymbolString(element, 'get'),
- argumentNodes: const Link<ast.Node>());
+ argumentNodes: const Link<ast.Node>(),
+ sourceInformation: sourceInformation);
}
/// Generate read access of an unresolved static or top level entity.
void generateStaticUnresolvedGet(ast.Send node, Element element) {
if (element is ErroneousElement) {
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGet(node);
// An erroneous element indicates an unresolved static getter.
handleInvalidStaticGet(node, element);
} else {
@@ -3350,7 +3386,8 @@
void generateStaticConstGet(
ast.Send node,
FieldElement field,
- ConstantExpression constant) {
+ ConstantExpression constant,
+ SourceInformation sourceInformation) {
ConstantValue value = backend.constants.getConstantValue(constant);
HConstant instruction;
// Constants that are referred via a deferred prefix should be referred
@@ -3358,9 +3395,11 @@
PrefixElement prefix = compiler.deferredLoadTask
.deferredPrefixElement(node, elements);
if (prefix != null) {
- instruction = graph.addDeferredConstant(value, prefix, compiler);
+ instruction =
+ graph.addDeferredConstant(value, prefix, sourceInformation, compiler);
} else {
- instruction = graph.addConstant(value, compiler);
+ instruction = graph.addConstant(
+ value, compiler, sourceInformation: sourceInformation);
}
stack.add(instruction);
// The inferrer may have found a better type than the constant
@@ -3382,34 +3421,41 @@
ConstantExpression constant =
backend.constants.getConstantForVariable(field);
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGet(node);
if (constant != null) {
if (!field.isAssignable) {
// A static final or const. Get its constant value and inline it if
// the value can be compiled eagerly.
- generateStaticConstGet(node, field, constant);
+ generateStaticConstGet(node, field, constant, sourceInformation);
} else {
// TODO(5346): Try to avoid the need for calling [declaration] before
// creating an [HStatic].
HInstruction instruction = new HStatic(
field.declaration,
- TypeMaskFactory.inferredTypeForElement(field, compiler));
+ TypeMaskFactory.inferredTypeForElement(field, compiler))
+ ..sourceInformation = sourceInformation;
push(instruction);
}
} else {
HInstruction instruction = new HLazyStatic(
field,
- TypeMaskFactory.inferredTypeForElement(field, compiler));
+ TypeMaskFactory.inferredTypeForElement(field, compiler))
+ ..sourceInformation = sourceInformation;
push(instruction);
}
}
/// Generate a getter invocation of the static or top level [getter].
void generateStaticGetterGet(ast.Send node, MethodElement getter) {
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGet(node);
if (getter.isDeferredLoaderGetter) {
- generateDeferredLoaderGet(node, getter);
+ generateDeferredLoaderGet(node, getter, sourceInformation);
} else {
generateIsDeferredLoadedCheckOfSend(node);
- pushInvokeStatic(node, getter, <HInstruction>[]);
+ pushInvokeStatic(node, getter, <HInstruction>[],
+ sourceInformation: sourceInformation);
}
}
@@ -3425,12 +3471,20 @@
generateIsDeferredLoadedCheckOfSend(node);
// TODO(5346): Try to avoid the need for calling [declaration] before
// creating an [HStatic].
- push(new HStatic(function.declaration, backend.nonNullType));
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGet(node);
+ push(new HStatic(function.declaration, backend.nonNullType)
+ ..sourceInformation = sourceInformation);
}
/// Read a local variable, function or parameter.
- void handleLocalGet(LocalElement local) {
- stack.add(localsHandler.readLocal(local));
+ void buildLocalGet(LocalElement local, SourceInformation sourceInformation) {
+ stack.add(localsHandler.readLocal(
+ local, sourceInformation: sourceInformation));
+ }
+
+ void handleLocalGet(ast.Send node, LocalElement local) {
+ buildLocalGet(local, sourceInformationBuilder.buildGet(node));
}
@override
@@ -3479,17 +3533,17 @@
@override
void visitLocalVariableGet(ast.Send node, LocalVariableElement variable, _) {
- handleLocalGet(variable);
+ handleLocalGet(node, variable);
}
@override
void visitParameterGet(ast.Send node, ParameterElement parameter, _) {
- handleLocalGet(parameter);
+ handleLocalGet(node, parameter);
}
@override
void visitLocalFunctionGet(ast.Send node, LocalFunctionElement function, _) {
- handleLocalGet(function);
+ handleLocalGet(node, function);
}
@override
@@ -3567,7 +3621,8 @@
location = send;
}
assert(selector.isSetter);
- pushInvokeDynamic(location, selector, mask, [receiver, value]);
+ pushInvokeDynamic(location, selector, mask, [receiver, value],
+ sourceInformation: sourceInformationBuilder.buildAssignment(location));
pop();
stack.add(value);
}
@@ -3617,7 +3672,9 @@
stack.add(checkedOrTrusted);
}
- localsHandler.updateLocal(local, checkedOrTrusted);
+ localsHandler.updateLocal(local, checkedOrTrusted,
+ sourceInformation:
+ sourceInformationBuilder.buildAssignment(location));
}
}
@@ -3827,12 +3884,15 @@
void _generateDynamicSend(ast.Send node, HInstruction receiver) {
Selector selector = elements.getSelector(node);
TypeMask mask = elements.getTypeMask(node);
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildCall(node, node.selector);
List<HInstruction> inputs = <HInstruction>[];
inputs.add(receiver);
addDynamicSendArgumentsToList(node, inputs);
- pushInvokeDynamic(node, selector, mask, inputs);
+ pushInvokeDynamic(node, selector, mask, inputs,
+ sourceInformation: sourceInformation);
if (selector.isSetter || selector.isIndexSet) {
pop();
stack.add(inputs.last);
@@ -3884,7 +3944,10 @@
ast.NodeList arguments,
Selector selector,
_) {
- generateCallInvoke(node, visitAndPop(expression));
+ generateCallInvoke(
+ node,
+ visitAndPop(expression),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -3893,7 +3956,10 @@
ast.NodeList arguments,
CallStructure callStructure,
_) {
- generateCallInvoke(node, localsHandler.readThis());
+ generateCallInvoke(
+ node,
+ localsHandler.readThis(),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -3903,7 +3969,10 @@
ast.NodeList arguments,
CallStructure callStructure,
_) {
- generateCallInvoke(node, localsHandler.readLocal(parameter));
+ generateCallInvoke(
+ node,
+ localsHandler.readLocal(parameter),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -3913,7 +3982,10 @@
ast.NodeList arguments,
CallStructure callStructure,
_) {
- generateCallInvoke(node, localsHandler.readLocal(variable));
+ generateCallInvoke(
+ node,
+ localsHandler.readLocal(variable),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -3923,7 +3995,10 @@
ast.NodeList arguments,
CallStructure callStructure,
_) {
- generateCallInvoke(node, localsHandler.readLocal(function));
+ generateCallInvoke(
+ node,
+ localsHandler.readLocal(function),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -3933,7 +4008,8 @@
ast.NodeList arguments,
CallStructure callStructure,
_) {
- generateCallInvoke(node, localsHandler.readLocal(function));
+ generateCallInvoke(node, localsHandler.readLocal(function),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
void handleForeignJs(ast.Send node) {
@@ -3953,17 +4029,21 @@
TypeMask ssaType =
TypeMaskFactory.fromNativeBehavior(nativeBehavior, compiler);
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildCall(node, node.argumentsNode);
if (nativeBehavior.codeTemplate.isExpression) {
push(new HForeignCode(
nativeBehavior.codeTemplate, ssaType, inputs,
effects: nativeBehavior.sideEffects,
- nativeBehavior: nativeBehavior));
+ nativeBehavior: nativeBehavior)
+ ..sourceInformation = sourceInformation);
} else {
push(new HForeignCode(
nativeBehavior.codeTemplate, ssaType, inputs,
isStatement: true,
effects: nativeBehavior.sideEffects,
- nativeBehavior: nativeBehavior));
+ nativeBehavior: nativeBehavior)
+ ..sourceInformation = sourceInformation);
}
}
@@ -4312,7 +4392,9 @@
}
}
- generateDeferredLoaderGet(ast.Send node, FunctionElement deferredLoader) {
+ generateDeferredLoaderGet(ast.Send node,
+ FunctionElement deferredLoader,
+ SourceInformation sourceInformation) {
// Until now we only handle these as getters.
invariant(node, deferredLoader.isDeferredLoaderGetter);
Element loadFunction = compiler.loadLibraryFunction;
@@ -4322,7 +4404,8 @@
var inputs = [graph.addConstantString(
new ast.DartString.literal(loadId), compiler)];
push(new HInvokeStatic(loadFunction, inputs, backend.nonNullType,
- targetCanThrow: false));
+ targetCanThrow: false)
+ ..sourceInformation = sourceInformation);
}
generateSuperNoSuchMethodSend(ast.Send node,
@@ -4371,14 +4454,16 @@
graph.addConstant(kindConstant, compiler),
argumentsInstruction,
argumentNamesInstruction],
- typeMask: backend.dynamicType);
+ typeMask: backend.dynamicType);
var inputs = <HInstruction>[pop()];
push(buildInvokeSuper(compiler.noSuchMethodSelector, element, inputs));
}
/// Generate a call to a super method or constructor.
- void generateSuperInvoke(ast.Send node, FunctionElement function) {
+ void generateSuperInvoke(ast.Send node,
+ FunctionElement function,
+ SourceInformation sourceInformation) {
// TODO(5347): Try to avoid the need for calling [implementation] before
// calling [makeStaticArgumentList].
Selector selector = elements.getSelector(node);
@@ -4389,29 +4474,37 @@
makeStaticArgumentList(selector.callStructure,
node.arguments,
function.implementation);
- push(buildInvokeSuper(selector, function, inputs));
+ push(buildInvokeSuper(selector, function, inputs, sourceInformation));
}
/// Access the value from the super [element].
void handleSuperGet(ast.Send node, Element element) {
Selector selector = elements.getSelector(node);
- push(buildInvokeSuper(selector, element, const <HInstruction>[]));
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildGet(node);
+ push(buildInvokeSuper(
+ selector, element, const <HInstruction>[], sourceInformation));
}
/// Invoke .call on the value retrieved from the super [element].
void handleSuperCallInvoke(ast.Send node, Element element) {
Selector selector = elements.getSelector(node);
- HInstruction target =
- buildInvokeSuper(selector, element, const <HInstruction>[]);
+ HInstruction target = buildInvokeSuper(
+ selector, element, const <HInstruction>[],
+ sourceInformationBuilder.buildGet(node));
add(target);
- generateCallInvoke(node, target);
+ generateCallInvoke(
+ node,
+ target,
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
/// Invoke super [method].
void handleSuperMethodInvoke(
ast.Send node,
MethodElement method) {
- generateSuperInvoke(node, method);
+ generateSuperInvoke(node, method,
+ sourceInformationBuilder.buildCall(node, node.selector));
}
/// Access an unresolved super property.
@@ -4622,8 +4715,10 @@
* extract the type argument by the index of the variable in the list of type
* variables for that class.
*/
- HInstruction readTypeVariable(ClassElement cls,
- TypeVariableElement variable) {
+ HInstruction readTypeVariable(
+ ClassElement cls,
+ TypeVariableElement variable,
+ {SourceInformation sourceInformation}) {
assert(sourceElement.isInstanceMember);
HInstruction target = localsHandler.readThis();
@@ -4641,11 +4736,15 @@
pushInvokeStatic(null,
backend.getGetRuntimeTypeArgument(),
[target, substitutionNameInstr, index],
- typeMask: backend.dynamicType);
+ typeMask: backend.dynamicType,
+ sourceInformation: sourceInformation);
} else {
- pushInvokeStatic(null, backend.getGetTypeArgumentByIndex(),
+ pushInvokeStatic(
+ null,
+ backend.getGetTypeArgumentByIndex(),
[target, index],
- typeMask: backend.dynamicType);
+ typeMask: backend.dynamicType,
+ sourceInformation: sourceInformation);
}
return pop();
}
@@ -4661,7 +4760,10 @@
/**
* Helper to create an instruction that gets the value of a type variable.
*/
- HInstruction addTypeVariableReference(TypeVariableType type) {
+ HInstruction addTypeVariableReference(
+ TypeVariableType type,
+ {SourceInformation sourceInformation}) {
+
assert(assertTypeInContext(type));
Element member = sourceElement;
bool isClosure = member.enclosingElement.isClosure;
@@ -4679,14 +4781,18 @@
// The type variable is used from a closure in a factory constructor.
// The value of the type argument is stored as a local on the closure
// itself.
- return localsHandler.readLocal(typeVariableLocal);
+ return localsHandler.readLocal(
+ typeVariableLocal, sourceInformation: sourceInformation);
} else if (member.isFunction ||
member.isGetter ||
member.isSetter ||
isInConstructorContext) {
// The type variable is stored on the "enclosing object" and needs to be
// accessed using the this-reference in the closure.
- return readTypeVariable(member.enclosingClass, type.element);
+ return readTypeVariable(
+ member.enclosingClass,
+ type.element,
+ sourceInformation: sourceInformation);
} else {
assert(member.isField);
// The type variable is stored in a parameter of the method.
@@ -4700,11 +4806,14 @@
// always return true when seeing one.
(member.isField && !isBuildingFor(member))) {
// The type variable is stored in a parameter of the method.
- return localsHandler.readLocal(typeVariableLocal);
+ return localsHandler.readLocal(
+ typeVariableLocal, sourceInformation: sourceInformation);
} else if (member.isInstanceMember) {
// The type variable is stored on the object.
- return readTypeVariable(member.enclosingClass,
- type.element);
+ return readTypeVariable(
+ member.enclosingClass,
+ type.element,
+ sourceInformation: sourceInformation);
} else {
compiler.internalError(type.element,
'Unexpected type variable in static context.');
@@ -4712,7 +4821,10 @@
}
}
- HInstruction analyzeTypeArgument(DartType argument) {
+ HInstruction analyzeTypeArgument(
+ DartType argument,
+ {SourceInformation sourceInformation}) {
+
assert(assertTypeInContext(argument));
if (argument.treatAsDynamic) {
// Represent [dynamic] as [null].
@@ -4720,7 +4832,8 @@
}
if (argument.isTypeVariable) {
- return addTypeVariableReference(argument);
+ return addTypeVariableReference(
+ argument, sourceInformation: sourceInformation);
}
List<HInstruction> inputs = <HInstruction>[];
@@ -4755,7 +4868,8 @@
void copyRuntimeTypeInfo(HInstruction source, HInstruction target) {
Element copyHelper = backend.getCopyTypeArguments();
- pushInvokeStatic(null, copyHelper, [source, target]);
+ pushInvokeStatic(null, copyHelper, [source, target],
+ sourceInformation: target.sourceInformation);
pop();
}
@@ -4775,7 +4889,8 @@
null,
typeInfoSetterElement,
<HInstruction>[newObject, typeInfo],
- typeMask: backend.dynamicType);
+ typeMask: backend.dynamicType,
+ sourceInformation: newObject.sourceInformation);
// The new object will now be referenced through the
// `setRuntimeTypeInfo` call. We therefore set the type of that
@@ -4933,6 +5048,8 @@
push(buildLiteralList(<HInstruction>[]));
stack.last.instructionType = elementType;
} else {
+ SourceInformation sourceInformation =
+ sourceInformationBuilder.buildNew(send);
ClassElement cls = constructor.enclosingClass;
if (cls.isAbstract && constructor.isGenerativeConstructor) {
generateAbstractClassInstantiationError(send, cls.name);
@@ -4942,7 +5059,9 @@
addInlinedInstantiation(expectedType);
pushInvokeStatic(node, constructor, inputs,
- typeMask: elementType, instanceType: expectedType);
+ typeMask: elementType,
+ instanceType: expectedType,
+ sourceInformation: sourceInformation);
removeInlinedInstantiation(expectedType);
}
HInstruction newInstance = stack.last;
@@ -4976,12 +5095,14 @@
}
void potentiallyAddTypeArguments(List<HInstruction> inputs, ClassElement cls,
- InterfaceType expectedType) {
+ InterfaceType expectedType,
+ {SourceInformation sourceInformation}) {
if (!backend.classNeedsRti(cls)) return;
assert(expectedType.typeArguments.isEmpty ||
cls.typeVariables.length == expectedType.typeArguments.length);
expectedType.typeArguments.forEach((DartType argument) {
- inputs.add(analyzeTypeArgument(argument));
+ inputs.add(analyzeTypeArgument(
+ argument, sourceInformation: sourceInformation));
});
}
@@ -5074,7 +5195,9 @@
new HIdentity(inputs[0], inputs[1], null, backend.boolType), node);
return;
} else {
- pushInvokeStatic(node, function, inputs);
+ pushInvokeStatic(node, function, inputs,
+ sourceInformation: sourceInformationBuilder.buildCall(
+ node, node.selector));
}
}
@@ -5093,7 +5216,10 @@
CallStructure callStructure,
_) {
generateStaticFieldGet(node, field);
- generateCallInvoke(node, pop());
+ generateCallInvoke(
+ node,
+ pop(),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -5124,7 +5250,10 @@
CallStructure callStructure,
_) {
generateStaticGetterGet(node, getter);
- generateCallInvoke(node, pop());
+ generateCallInvoke(
+ node,
+ pop(),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -5135,7 +5264,10 @@
CallStructure callStructure,
_) {
generateStaticFieldGet(node, field);
- generateCallInvoke(node, pop());
+ generateCallInvoke(
+ node,
+ pop(),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -5170,7 +5302,10 @@
CallStructure callStructure,
_) {
generateStaticGetterGet(node, getter);
- generateCallInvoke(node, pop());
+ generateCallInvoke(
+ node,
+ pop(),
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
@override
@@ -5335,7 +5470,8 @@
void generateTypeVariableLiteral(ast.Send node,
TypeVariableType typeVariable) {
DartType type = localsHandler.substInContext(typeVariable);
- HInstruction value = analyzeTypeArgument(type);
+ HInstruction value = analyzeTypeArgument(type,
+ sourceInformation: sourceInformationBuilder.buildGet(node));
pushInvokeStatic(node,
backend.getRuntimeTypeToString(),
[value],
@@ -5352,19 +5488,21 @@
// reference instead of creating a NoSuchMethodError to avoid pulling it
// in if it is not used (e.g., in a try/catch).
HInstruction target = pop();
- generateCallInvoke(node, target);
+ generateCallInvoke(node, target,
+ sourceInformationBuilder.buildCall(node, node.argumentsNode));
}
/// Generate a '.call' invocation on [target].
- void generateCallInvoke(ast.Send node, HInstruction target) {
+ void generateCallInvoke(ast.Send node,
+ HInstruction target,
+ SourceInformation sourceInformation) {
Selector selector = elements.getSelector(node);
List<HInstruction> inputs = <HInstruction>[target];
addDynamicSendArgumentsToList(node, inputs);
- pushWithPosition(
- new HInvokeClosure(
+ push(new HInvokeClosure(
new Selector.callClosureFrom(selector),
- inputs, backend.dynamicType),
- node);
+ inputs, backend.dynamicType)
+ ..sourceInformation = sourceInformation);
}
visitGetterSend(ast.Send node) {
@@ -5399,7 +5537,8 @@
String methodName,
{Link<ast.Node> argumentNodes,
List<HInstruction> argumentValues,
- List<String> existingArguments}) {
+ List<String> existingArguments,
+ SourceInformation sourceInformation}) {
Element helper = backend.getThrowNoSuchMethod();
ConstantValue receiverConstant =
constantSystem.createString(new ast.DartString.empty());
@@ -5432,7 +5571,8 @@
}
pushInvokeStatic(diagnosticNode,
helper,
- [receiver, name, arguments, existingNamesList]);
+ [receiver, name, arguments, existingNamesList],
+ sourceInformation: sourceInformation);
}
/**
@@ -5511,8 +5651,7 @@
Selector selector,
TypeMask mask,
List<HInstruction> arguments,
- {ast.Node location}) {
- if (location == null) location = node;
+ {SourceInformation sourceInformation}) {
// We prefer to not inline certain operations on indexables,
// because the constant folder will handle them better and turn
@@ -5571,17 +5710,17 @@
TypeMask type =
TypeMaskFactory.inferredTypeForSelector(selector, mask, compiler);
if (selector.isGetter) {
- pushWithPosition(
- new HInvokeDynamicGetter(selector, mask, null, inputs, type),
- location);
+ push(
+ new HInvokeDynamicGetter(selector, mask, null, inputs, type)
+ ..sourceInformation = sourceInformation);
} else if (selector.isSetter) {
- pushWithPosition(
- new HInvokeDynamicSetter(selector, mask, null, inputs, type),
- location);
+ push(
+ new HInvokeDynamicSetter(selector, mask, null, inputs, type)
+ ..sourceInformation = sourceInformation);
} else {
- pushWithPosition(
- new HInvokeDynamicMethod(selector, mask, inputs, type, isIntercepted),
- location);
+ push(
+ new HInvokeDynamicMethod(selector, mask, inputs, type, isIntercepted)
+ ..sourceInformation = sourceInformation);
}
}
@@ -5589,7 +5728,9 @@
Element element,
List<HInstruction> arguments,
{TypeMask typeMask,
- InterfaceType instanceType}) {
+ InterfaceType instanceType,
+ SourceInformation sourceInformation}) {
+ // TODO(johnniwinther): Use [sourceInformation] instead of [location].
if (tryInlineMethod(element, null, null, arguments, location,
instanceType: instanceType)) {
return;
@@ -5604,7 +5745,8 @@
// creating an [HInvokeStatic].
HInvokeStatic instruction = new HInvokeStatic(
element.declaration, arguments, typeMask,
- targetCanThrow: targetCanThrow);
+ targetCanThrow: targetCanThrow)
+ ..sourceInformation = sourceInformation;
if (!currentInlinedInstantiations.isEmpty) {
instruction.instantiatedTypes = new List<DartType>.from(
currentInlinedInstantiations);
@@ -5619,7 +5761,8 @@
HInstruction buildInvokeSuper(Selector selector,
Element element,
- List<HInstruction> arguments) {
+ List<HInstruction> arguments,
+ [SourceInformation sourceInformation]) {
HInstruction receiver = localsHandler.readThis();
// TODO(5346): Try to avoid the need for calling [declaration] before
// creating an [HStatic].
@@ -5644,6 +5787,7 @@
selector,
inputs,
type,
+ sourceInformation,
isSetter: selector.isSetter || selector.isIndexSet);
instruction.sideEffects =
compiler.world.getSideEffectsOfSelector(selector, null);
@@ -5661,11 +5805,14 @@
assert(arguments.tail.isEmpty);
rhs = pop();
}
- visitBinarySend(receiver, rhs,
- elements.getOperatorSelectorInComplexSendSet(node),
- elements.getOperatorTypeMaskInComplexSendSet(node),
- node,
- location: node.assignmentOperator);
+ visitBinarySend(
+ receiver,
+ rhs,
+ elements.getOperatorSelectorInComplexSendSet(node),
+ elements.getOperatorTypeMaskInComplexSendSet(node),
+ node,
+ sourceInformation:
+ sourceInformationBuilder.buildGeneric(node.assignmentOperator));
}
void handleSuperSendSet(ast.SendSet node) {
@@ -6525,7 +6672,7 @@
} else if (getter.isFunction) {
generateStaticFunctionGet(node, getter);
} else if (getter.isLocal) {
- handleLocalGet(getter);
+ handleLocalGet(node, getter);
} else {
internalError(node, "Unexpected getter: $getter");
}
@@ -6665,7 +6812,10 @@
'rethrowableException should not be null.');
}
handleInTryStatement();
- closeAndGotoExit(new HThrow(exception, isRethrow: true));
+ closeAndGotoExit(
+ new HThrow(exception,
+ sourceInformationBuilder.buildThrow(node),
+ isRethrow: true));
}
visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
@@ -6762,7 +6912,8 @@
visitThrowExpression(node.expression);
if (isReachable) {
handleInTryStatement();
- push(new HThrowExpression(pop()));
+ push(new HThrowExpression(
+ pop(), sourceInformationBuilder.buildThrow(node)));
isReachable = false;
}
}
@@ -7536,7 +7687,10 @@
[localsHandler.readLocal(switchTarget)],
nativeBehavior: native.NativeBehavior.PURE));
}
- handleIf(node, buildCondition, buildLoop, () => {});
+ handleIf(node,
+ visitCondition: buildCondition,
+ visitThen: buildLoop,
+ visitElse: () => {});
}
}
@@ -7599,7 +7753,7 @@
if (caseIterator.hasNext) {
pushInvokeStatic(switchCase, getFallThroughErrorElement, []);
HInstruction error = pop();
- closeAndGotoExit(new HThrow(error));
+ closeAndGotoExit(new HThrow(error, error.sourceInformation));
} else if (!isDefaultCase(switchCase)) {
// If there is no default, we will add one later to avoid
// the critical edge. So we generate a break statement to make
@@ -7875,17 +8029,24 @@
void visitElse() {
if (link.isEmpty) {
- closeAndGotoExit(new HThrow(exception, isRethrow: true));
+ closeAndGotoExit(
+ new HThrow(exception,
+ exception.sourceInformation,
+ isRethrow: true));
} else {
ast.CatchBlock newBlock = link.head;
handleIf(node,
- () { pushCondition(newBlock); },
- visitThen, visitElse);
+ visitCondition: () { pushCondition(newBlock); },
+ visitThen: visitThen,
+ visitElse: visitElse);
}
}
ast.CatchBlock firstBlock = link.head;
- handleIf(node, () { pushCondition(firstBlock); }, visitThen, visitElse);
+ handleIf(node,
+ visitCondition: () { pushCondition(firstBlock); },
+ visitThen: visitThen,
+ visitElse: visitElse);
if (!isAborted()) endCatchBlock = close(new HGoto());
rethrowableException = oldRethrowableException;
@@ -8016,7 +8177,8 @@
void emitReturn(HInstruction value, ast.Node node) {
if (inliningStack.isEmpty) {
- closeAndGotoExit(attachPosition(new HReturn(value), node));
+ closeAndGotoExit(new HReturn(value,
+ sourceInformationBuilder.buildReturn(node)));
} else {
localsHandler.updateLocal(returnLocal, value);
}
@@ -8375,13 +8537,14 @@
void buildCondition(void visitCondition(),
SsaBranch conditionBranch,
SsaBranch thenBranch,
- SsaBranch elseBranch) {
+ SsaBranch elseBranch,
+ SourceInformation sourceInformation) {
startBranch(conditionBranch);
visitCondition();
checkNotAborted();
assert(identical(builder.current, builder.lastOpenedBlock));
HInstruction conditionValue = builder.popBoolified();
- HIf branch = new HIf(conditionValue);
+ HIf branch = new HIf(conditionValue)..sourceInformation = sourceInformation;
HBasicBlock conditionExitBlock = builder.current;
builder.close(branch);
conditionBranch.exitLocals = builder.localsHandler;
@@ -8442,7 +8605,10 @@
return null;
}
- handleIf(void visitCondition(), void visitThen(), void visitElse()) {
+ handleIf(void visitCondition(),
+ void visitThen(),
+ void visitElse(),
+ {SourceInformation sourceInformation}) {
if (visitElse == null) {
// Make sure to have an else part to avoid a critical edge. A
// critical edge is an edge that connects a block with multiple
@@ -8452,12 +8618,17 @@
visitElse = () {};
}
- _handleDiamondBranch(visitCondition, visitThen, visitElse, false);
+ _handleDiamondBranch(
+ visitCondition, visitThen, visitElse, isExpression: false,
+ sourceInformation: sourceInformation);
}
- handleConditional(void visitCondition(), void visitThen(), void visitElse()) {
+ handleConditional(void visitCondition(),
+ void visitThen(),
+ void visitElse()) {
assert(visitElse != null);
- _handleDiamondBranch(visitCondition, visitThen, visitElse, true);
+ _handleDiamondBranch(
+ visitCondition, visitThen, visitElse, isExpression: true);
}
handleIfNull(void left(), void right()) {
@@ -8552,7 +8723,8 @@
void _handleDiamondBranch(void visitCondition(),
void visitThen(),
void visitElse(),
- bool isExpression) {
+ {bool isExpression,
+ SourceInformation sourceInformation}) {
SsaBranch conditionBranch = new SsaBranch(this);
SsaBranch thenBranch = new SsaBranch(this);
SsaBranch elseBranch = new SsaBranch(this);
@@ -8561,7 +8733,8 @@
conditionBranch.startLocals = builder.localsHandler;
builder.goto(builder.current, conditionBranch.block);
- buildCondition(visitCondition, conditionBranch, thenBranch, elseBranch);
+ buildCondition(visitCondition, conditionBranch, thenBranch, elseBranch,
+ sourceInformation);
HInstruction thenValue =
buildBranch(thenBranch, visitThen, joinBranch, isExpression);
HInstruction elseValue =
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 2524fdf..6057d6f 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -7,7 +7,7 @@
class SsaCodeGeneratorTask extends CompilerTask {
final JavaScriptBackend backend;
- final SourceInformationFactory sourceInformationFactory;
+ final SourceInformationStrategy sourceInformationFactory;
SsaCodeGeneratorTask(JavaScriptBackend backend,
this.sourceInformationFactory)
@@ -17,12 +17,6 @@
String get name => 'SSA code generator';
NativeEmitter get nativeEmitter => backend.emitter.nativeEmitter;
-
- js.Node attachPosition(js.Node node, AstElement element) {
- return node.withSourceInformation(
- StartEndSourceInformation.computeSourceInformation(element));
- }
-
js.Fun buildJavaScriptFunction(FunctionElement element,
List<js.Parameter> parameters,
js.Block body) {
@@ -35,8 +29,9 @@
: const js.AsyncModifier.sync());
return new js.Fun(parameters, body, asyncModifier: asyncModifier)
- .withSourceInformation(sourceInformationFactory.forContext(element)
- .buildDeclaration(element));
+ .withSourceInformation(
+ sourceInformationFactory.createBuilderForContext(element)
+ .buildDeclaration(element));
}
js.Expression generateCode(CodegenWorkItem work, HGraph graph) {
@@ -51,7 +46,7 @@
return measure(() {
compiler.tracer.traceGraph("codegen", graph);
SourceInformation sourceInformation =
- sourceInformationFactory.forContext(work.element)
+ sourceInformationFactory.createBuilderForContext(work.element)
.buildDeclaration(work.element);
SsaCodeGenerator codegen = new SsaCodeGenerator(backend, work);
codegen.visitGraph(graph);
@@ -189,11 +184,8 @@
* If the [instruction] is not `null` it will be used to attach the position
* to the [statement].
*/
- void pushStatement(js.Statement statement, [HInstruction instruction]) {
+ void pushStatement(js.Statement statement) {
assert(expressionStack.isEmpty);
- if (instruction != null) {
- statement = attachLocation(statement, instruction);
- }
currentContainer.statements.add(statement);
}
@@ -206,18 +198,16 @@
* to the [expression].
*/
pushExpressionAsStatement(js.Expression expression,
- [HInstruction instruction]) {
- pushStatement(new js.ExpressionStatement(expression), instruction);
+ SourceInformation sourceInformation) {
+ pushStatement(new js.ExpressionStatement(expression)
+ .withSourceInformation(sourceInformation));
}
/**
* If the [instruction] is not `null` it will be used to attach the position
* to the [expression].
*/
- push(js.Expression expression, [HInstruction instruction]) {
- if (instruction != null) {
- expression = attachLocation(expression, instruction);
- }
+ push(js.Expression expression) {
expressionStack.add(expression);
}
@@ -225,21 +215,6 @@
return expressionStack.removeLast();
}
- attachLocationToLast(HInstruction instruction) {
- int index = expressionStack.length - 1;
- expressionStack[index] =
- attachLocation(expressionStack[index], instruction);
- }
-
- js.Node attachLocation(js.Node jsNode, HInstruction instruction) {
- return attachSourceInformation(jsNode, instruction.sourceInformation);
- }
-
- js.Node attachSourceInformation(js.Node jsNode,
- SourceInformation sourceInformation) {
- return jsNode.withSourceInformation(sourceInformation);
- }
-
void preGenerateMethod(HGraph graph) {
new SsaInstructionSelection(compiler).visitGraph(graph);
new SsaTypeKnownRemover().visitGraph(graph);
@@ -499,10 +474,13 @@
}
}
}
- return new js.Assignment(new js.VariableUse(variableName), value);
+ return new js.Assignment(new js.VariableUse(variableName), value)
+ .withSourceInformation(value.sourceInformation);
}
- void assignVariable(String variableName, js.Expression value) {
+ void assignVariable(String variableName,
+ js.Expression value,
+ SourceInformation sourceInformation) {
if (isGeneratingExpression) {
// If we are in an expression then we can't declare the variable here.
// We have no choice, but to use it and then declare it separately.
@@ -522,7 +500,8 @@
new js.VariableInitialization(decl, value);
pushExpressionAsStatement(new js.VariableDeclarationList(
- <js.VariableInitialization>[initialization]));
+ <js.VariableInitialization>[initialization]),
+ sourceInformation);
} else {
// Otherwise we are just going to use it. If we have not already declared
// it then we make sure we will declare it later.
@@ -530,7 +509,8 @@
collectedVariableDeclarations.add(variableName);
}
pushExpressionAsStatement(
- generateExpressionAssignment(variableName, value));
+ generateExpressionAssignment(variableName, value),
+ sourceInformation);
}
}
@@ -553,7 +533,8 @@
if (needsAssignment &&
!instruction.isControlFlow() && variableNames.hasName(instruction)) {
visitExpression(instruction);
- assignVariable(variableNames.getName(instruction), pop());
+ assignVariable(variableNames.getName(instruction), pop(),
+ instruction.sourceInformation);
return;
}
@@ -594,7 +575,8 @@
visit(node);
if (!expressionStack.isEmpty) {
assert(expressionStack.length == 1);
- pushExpressionAsStatement(pop());
+ js.Expression expression = pop();
+ pushExpressionAsStatement(expression, node.sourceInformation);
}
}
@@ -611,7 +593,8 @@
pushStatement(new js.Break(backend.namer.implicitBreakLabelName(target)));
}
- js.Statement wrapIntoLabels(js.Statement result, List<LabelDefinition> labels) {
+ js.Statement wrapIntoLabels(js.Statement result,
+ List<LabelDefinition> labels) {
for (LabelDefinition label in labels) {
if (label.isTarget) {
String breakLabelString = backend.namer.breakLabelName(label);
@@ -834,7 +817,8 @@
visitBodyIgnoreLabels(info);
currentContainer = oldContainer;
body = unwrapStatement(body);
- loop = new js.For(jsInitialization, jsCondition, jsUpdates, body);
+ loop = new js.For(jsInitialization, jsCondition, jsUpdates, body)
+ .withSourceInformation(info.sourceInformation);
} else {
// We have either no update graph, or it's too complex to
// put in an expression.
@@ -848,7 +832,7 @@
jsCondition = generateExpression(condition);
currentContainer = body;
} else {
- jsCondition = newLiteralBool(true);
+ jsCondition = newLiteralBool(true, info.sourceInformation);
currentContainer = body;
generateStatements(condition);
use(condition.conditionExpression);
@@ -871,7 +855,8 @@
}
currentContainer = oldContainer;
body = unwrapStatement(body);
- loop = new js.While(jsCondition, body);
+ loop = new js.While(jsCondition, body)
+ .withSourceInformation(info.sourceInformation);
}
break;
case HLoopBlockInformation.DO_WHILE_LOOP:
@@ -922,7 +907,10 @@
// If the condition is dead code, we turn the do-while into
// a simpler while because we will never reach the condition
// at the end of the loop anyway.
- loop = new js.While(newLiteralBool(true), unwrapStatement(body));
+ loop = new js.While(
+ newLiteralBool(true, info.sourceInformation),
+ unwrapStatement(body))
+ .withSourceInformation(info.sourceInformation);
} else {
if (hasPhiUpdates || hasExitPhiUpdates) {
updateBody.statements.add(new js.Continue(null));
@@ -936,9 +924,10 @@
}
body.statements.add(
new js.If(jsCondition, updateBody, exitLoop));
- jsCondition = newLiteralBool(true);
+ jsCondition = newLiteralBool(true, info.sourceInformation);
}
- loop = new js.Do(unwrapStatement(body), jsCondition);
+ loop = new js.Do(unwrapStatement(body), jsCondition)
+ .withSourceInformation(info.sourceInformation);
}
currentContainer = oldContainer;
break;
@@ -946,7 +935,7 @@
compiler.internalError(condition.conditionExpression,
'Unexpected loop kind: ${info.kind}.');
}
- js.Statement result = attachSourceInformation(loop, info.sourceInformation);
+ js.Statement result = loop;
if (info.kind == HLoopBlockInformation.SWITCH_CONTINUE_LOOP) {
String continueLabelString =
backend.namer.implicitContinueLabelName(info.target);
@@ -1093,7 +1082,7 @@
}
void emitAssignment(String destination, String source) {
- assignVariable(destination, new js.VariableUse(source));
+ assignVariable(destination, new js.VariableUse(source), null);
}
/**
@@ -1193,7 +1182,7 @@
for (Copy copy in handler.assignments) {
String name = variableNames.getName(copy.destination);
use(copy.source);
- assignVariable(name, pop());
+ assignVariable(name, pop(), null);
}
}
@@ -1209,27 +1198,38 @@
visit(instruction);
}
- visitInvokeBinary(HInvokeBinary node, String op) {
+ void handleInvokeBinary(HInvokeBinary node,
+ String op,
+ SourceInformation sourceInformation) {
use(node.left);
js.Expression jsLeft = pop();
use(node.right);
- push(new js.Binary(op, jsLeft, pop()), node);
+ push(new js.Binary(op, jsLeft, pop())
+ .withSourceInformation(sourceInformation));
}
- visitRelational(HRelational node, String op) => visitInvokeBinary(node, op);
+ visitInvokeBinary(HInvokeBinary node, String op) {
+ handleInvokeBinary(node, op, node.sourceInformation);
+ }
+
+ visitRelational(HRelational node, String op) {
+ handleInvokeBinary(node, op, node.sourceInformation);
+ }
// We want the outcome of bit-operations to be positive. We use the unsigned
// shift operator to achieve this.
visitBitInvokeBinary(HBinaryBitOp node, String op) {
visitInvokeBinary(node, op);
if (op != '>>>' && requiresUintConversion(node)) {
- push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node);
+ push(new js.Binary(">>>", pop(), new js.LiteralNumber("0"))
+ .withSourceInformation(node.sourceInformation));
}
}
visitInvokeUnary(HInvokeUnary node, String op) {
use(node.operand);
- push(new js.Prefix(op, pop()), node);
+ push(new js.Prefix(op, pop())
+ .withSourceInformation(node.sourceInformation));
}
// We want the outcome of bit-operations to be positive. We use the unsigned
@@ -1237,11 +1237,14 @@
visitBitInvokeUnary(HInvokeUnary node, String op) {
visitInvokeUnary(node, op);
if (requiresUintConversion(node)) {
- push(new js.Binary(">>>", pop(), new js.LiteralNumber("0")), node);
+ push(new js.Binary(">>>", pop(), new js.LiteralNumber("0"))
+ .withSourceInformation(node.sourceInformation));
}
}
- void emitIdentityComparison(HIdentity instruction, bool inverse) {
+ void emitIdentityComparison(HIdentity instruction,
+ SourceInformation sourceInformation,
+ {bool inverse: false}) {
String op = instruction.singleComparisonOp;
HInstruction left = instruction.left;
HInstruction right = instruction.right;
@@ -1249,7 +1252,8 @@
use(left);
js.Expression jsLeft = pop();
use(right);
- push(new js.Binary(mapRelationalOperator(op, inverse), jsLeft, pop()));
+ push(new js.Binary(mapRelationalOperator(op, inverse), jsLeft, pop())
+ .withSourceInformation(sourceInformation));
} else {
assert(NullConstantValue.JsNull == 'null');
use(left);
@@ -1264,12 +1268,13 @@
js.Binary tripleEq = new js.Binary(mapRelationalOperator("===", inverse),
pop(), pop());
- push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq));
+ push(new js.Conditional(leftEqualsNull, rightEqualsNull, tripleEq)
+ .withSourceInformation(sourceInformation));
}
}
visitIdentity(HIdentity node) {
- emitIdentityComparison(node, false);
+ emitIdentityComparison(node, node.sourceInformation, inverse: false);
}
visitAdd(HAdd node) => visitInvokeBinary(node, '+');
@@ -1292,11 +1297,13 @@
use(node.left);
js.Expression jsLeft = pop();
use(node.right);
- push(new js.Binary('/', jsLeft, pop()), node);
- push(new js.Binary('|', pop(), new js.LiteralNumber("0")), node);
+ push(new js.Binary('/', jsLeft, pop())
+ .withSourceInformation(node.sourceInformation));
+ push(new js.Binary('|', pop(), new js.LiteralNumber("0"))
+ .withSourceInformation(node.sourceInformation));
}
- visitNegate(HNegate node) => visitInvokeUnary(node, '-');
+ visitNegate(HNegate node) => visitInvokeUnary(node, '-');
visitLess(HLess node) => visitRelational(node, '<');
visitLessEqual(HLessEqual node) => visitRelational(node, '<=');
@@ -1306,7 +1313,9 @@
visitBoolify(HBoolify node) {
assert(node.inputs.length == 1);
use(node.inputs[0]);
- push(new js.Binary('===', pop(), newLiteralBool(true)), node);
+ push(new js.Binary('===', pop(),
+ newLiteralBool(true, node.sourceInformation))
+ .withSourceInformation(node.sourceInformation));
}
visitExit(HExit node) {
@@ -1362,16 +1371,20 @@
if (node.label != null) {
LabelDefinition label = node.label;
if (!tryCallAction(breakAction, label)) {
- pushStatement(new js.Break(backend.namer.breakLabelName(label)), node);
+ pushStatement(
+ new js.Break(backend.namer.breakLabelName(label))
+ .withSourceInformation(node.sourceInformation));
}
} else {
JumpTarget target = node.target;
if (!tryCallAction(breakAction, target)) {
if (node.breakSwitchContinueLoop) {
- pushStatement(new js.Break(
- backend.namer.implicitContinueLabelName(target)), node);
+ pushStatement(
+ new js.Break(backend.namer.implicitContinueLabelName(target))
+ .withSourceInformation(node.sourceInformation));
} else {
- pushStatement(new js.Break(null), node);
+ pushStatement(new js.Break(null)
+ .withSourceInformation(node.sourceInformation));
}
}
}
@@ -1383,17 +1396,20 @@
LabelDefinition label = node.label;
if (!tryCallAction(continueAction, label)) {
// TODO(floitsch): should this really be the breakLabelName?
- pushStatement(new js.Continue(backend.namer.breakLabelName(label)),
- node);
+ pushStatement(
+ new js.Continue(backend.namer.breakLabelName(label))
+ .withSourceInformation(node.sourceInformation));
}
} else {
JumpTarget target = node.target;
if (!tryCallAction(continueAction, target)) {
if (target.statement is ast.SwitchStatement) {
- pushStatement(new js.Continue(
- backend.namer.implicitContinueLabelName(target)), node);
+ pushStatement(
+ new js.Continue(backend.namer.implicitContinueLabelName(target))
+ .withSourceInformation(node.sourceInformation));
} else {
- pushStatement(new js.Continue(null), node);
+ pushStatement(new js.Continue(null)
+ .withSourceInformation(node.sourceInformation));
}
}
}
@@ -1444,7 +1460,8 @@
js.Statement elsePart =
unwrapStatement(generateStatementsInNewBlock(elseGraph));
- pushStatement(new js.If(test, thenPart, elsePart), node);
+ pushStatement(new js.If(test, thenPart, elsePart)
+ .withSourceInformation(node.sourceInformation));
}
visitIf(HIf node) {
@@ -1499,7 +1516,8 @@
backend.namer.globalObjectFor(backend.interceptorsLibrary));
use(node.receiver);
List<js.Expression> arguments = <js.Expression>[pop()];
- push(js.propertyCall(isolate, name, arguments), node);
+ push(js.propertyCall(isolate, name, arguments)
+ .withSourceInformation(node.sourceInformation));
registry.registerUseInterceptor();
}
}
@@ -1538,7 +1556,8 @@
} else {
methodLiteral = backend.namer.asName(methodName);
}
- push(js.propertyCall(object, methodLiteral, arguments), node);
+ push(js.propertyCall(object, methodLiteral, arguments)
+ .withSourceInformation(node.sourceInformation));
}
void visitInvokeConstructorBody(HInvokeConstructorBody node) {
@@ -1546,7 +1565,8 @@
js.Expression object = pop();
js.Name methodName = backend.namer.instanceMethodName(node.element);
List<js.Expression> arguments = visitArguments(node.inputs);
- push(js.propertyCall(object, methodName, arguments), node);
+ push(js.propertyCall(object, methodName, arguments)
+ .withSourceInformation(node.sourceInformation));
registry.registerStaticUse(node.element);
}
@@ -1557,7 +1577,8 @@
Selector selector = node.selector;
TypeMask mask = getOptimizedSelectorFor(node, selector, node.mask);
js.Name methodName = backend.registerOneShotInterceptor(selector);
- push(js.propertyCall(isolate, methodName, arguments), node);
+ push(js.propertyCall(isolate, methodName, arguments)
+ .withSourceInformation(node.sourceInformation));
if (selector.isGetter) {
registerGetter(node);
} else if (selector.isSetter) {
@@ -1622,14 +1643,16 @@
visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
use(node.receiver);
js.Name name = backend.namer.invocationName(node.selector);
- push(js.propertyCall(pop(), name, visitArguments(node.inputs)), node);
+ push(js.propertyCall(pop(), name, visitArguments(node.inputs))
+ .withSourceInformation(node.sourceInformation));
registerSetter(node);
}
visitInvokeDynamicGetter(HInvokeDynamicGetter node) {
use(node.receiver);
js.Name name = backend.namer.invocationName(node.selector);
- push(js.propertyCall(pop(), name, visitArguments(node.inputs)), node);
+ push(js.propertyCall(pop(), name, visitArguments(node.inputs))
+ .withSourceInformation(node.sourceInformation));
registerGetter(node);
}
@@ -1638,8 +1661,8 @@
use(node.receiver);
push(js.propertyCall(pop(),
backend.namer.invocationName(call),
- visitArguments(node.inputs)),
- node);
+ visitArguments(node.inputs))
+ .withSourceInformation(node.sourceInformation));
registry.registerDynamicInvocation(
new UniverseSelector(call, null));
}
@@ -1681,7 +1704,8 @@
} else {
registry.registerStaticInvocation(element);
push(backend.emitter.staticFunctionAccess(element));
- push(new js.Call(pop(), arguments), node);
+ push(new js.Call(pop(), arguments,
+ sourceInformation: node.sourceInformation));
}
}
@@ -1694,12 +1718,15 @@
js.Name fieldName =
backend.namer.instanceFieldPropertyName(superMethod);
use(node.inputs[0]);
- js.PropertyAccess access = new js.PropertyAccess(pop(), fieldName);
+ js.PropertyAccess access =
+ new js.PropertyAccess(pop(), fieldName)
+ .withSourceInformation(node.sourceInformation);
if (node.isSetter) {
use(node.value);
- push(new js.Assignment(access, pop()), node);
+ push(new js.Assignment(access, pop())
+ .withSourceInformation(node.sourceInformation));
} else {
- push(access, node);
+ push(access);
}
} else {
Selector selector = node.selector;
@@ -1725,15 +1752,15 @@
push(js.js('#.#.call(#)',
[backend.emitter.prototypeAccess(superClass,
hasBeenInstantiated: true),
- methodName, visitArguments(node.inputs, start: 0)]),
- node);
+ methodName, visitArguments(node.inputs, start: 0)])
+ .withSourceInformation(node.sourceInformation));
} else {
use(node.receiver);
push(
js.js('#.#(#)', [
pop(), backend.namer.aliasedSuperMemberPropertyName(superMethod),
- visitArguments(node.inputs, start: 1)]), // Skip receiver argument.
- node);
+ visitArguments(node.inputs, start: 1)]) // Skip receiver argument.
+ .withSourceInformation(node.sourceInformation));
}
}
}
@@ -1745,15 +1772,18 @@
// We access a JavaScript member we know all objects besides
// null and undefined have: V8 does not like accessing a member
// that does not exist.
- push(new js.PropertyAccess.field(pop(), 'toString'), node);
+ push(new js.PropertyAccess.field(pop(), 'toString')
+ .withSourceInformation(node.sourceInformation));
} else if (element == backend.jsIndexableLength) {
// We're accessing a native JavaScript property called 'length'
// on a JS String or a JS array. Therefore, the name of that
// property should not be mangled.
- push(new js.PropertyAccess.field(pop(), 'length'), node);
+ push(new js.PropertyAccess.field(pop(), 'length')
+ .withSourceInformation(node.sourceInformation));
} else {
js.Name name = backend.namer.instanceFieldPropertyName(element);
- push(new js.PropertyAccess(pop(), name), node);
+ push(new js.PropertyAccess(pop(), name)
+ .withSourceInformation(node.sourceInformation));
registry.registerFieldGetter(element);
}
}
@@ -1765,8 +1795,8 @@
use(node.receiver);
js.Expression receiver = pop();
use(node.value);
- push(new js.Assignment(new js.PropertyAccess(receiver, name), pop()),
- node);
+ push(new js.Assignment(new js.PropertyAccess(receiver, name), pop())
+ .withSourceInformation(node.sourceInformation));
}
visitReadModifyWrite(HReadModifyWrite node) {
@@ -1776,12 +1806,15 @@
use(node.receiver);
js.Expression fieldReference = new js.PropertyAccess(pop(), name);
if (node.isPreOp) {
- push(new js.Prefix(node.jsOp, fieldReference), node);
+ push(new js.Prefix(node.jsOp, fieldReference)
+ .withSourceInformation(node.sourceInformation));
} else if (node.isPostOp) {
- push(new js.Postfix(node.jsOp, fieldReference), node);
+ push(new js.Postfix(node.jsOp, fieldReference)
+ .withSourceInformation(node.sourceInformation));
} else {
use(node.value);
- push(new js.Assignment.compound(fieldReference, node.jsOp, pop()), node);
+ push(new js.Assignment.compound(fieldReference, node.jsOp, pop())
+ .withSourceInformation(node.sourceInformation));
}
}
@@ -1791,7 +1824,9 @@
visitLocalSet(HLocalSet node) {
use(node.value);
- assignVariable(variableNames.getName(node.receiver), pop());
+ assignVariable(variableNames.getName(node.receiver),
+ pop(),
+ node.sourceInformation);
}
void registerForeignTypes(HForeign node) {
@@ -1812,14 +1847,16 @@
use(inputs[i]);
interpolatedExpressions.add(pop());
}
- pushStatement(node.codeTemplate.instantiate(interpolatedExpressions));
+ pushStatement(node.codeTemplate.instantiate(interpolatedExpressions)
+ .withSourceInformation(node.sourceInformation));
} else {
List<js.Expression> interpolatedExpressions = <js.Expression>[];
for (int i = 0; i < inputs.length; i++) {
use(inputs[i]);
interpolatedExpressions.add(pop());
}
- push(node.codeTemplate.instantiate(interpolatedExpressions));
+ push(node.codeTemplate.instantiate(interpolatedExpressions)
+ .withSourceInformation(node.sourceInformation));
}
// TODO(sra): Tell world.nativeEnqueuer about the types created here.
@@ -1830,7 +1867,8 @@
js.Expression jsClassReference =
backend.emitter.constructorAccess(node.element);
List<js.Expression> arguments = visitArguments(node.inputs, start: 0);
- push(new js.New(jsClassReference, arguments), node);
+ push(new js.New(jsClassReference, arguments)
+ .withSourceInformation(node.sourceInformation));
registerForeignTypes(node);
// We also use ForeignNew to instantiate closure classes that belong to
// function expressions. We have to register their use here, as otherwise
@@ -1846,16 +1884,20 @@
});
}
- js.Expression newLiteralBool(bool value) {
+ js.Expression newLiteralBool(bool value,
+ SourceInformation sourceInformation) {
if (compiler.enableMinification) {
// Use !0 for true, !1 for false.
- return new js.Prefix("!", new js.LiteralNumber(value ? "0" : "1"));
+ return new js.Prefix("!", new js.LiteralNumber(value ? "0" : "1"))
+ .withSourceInformation(sourceInformation);
} else {
- return new js.LiteralBool(value);
+ return new js.LiteralBool(value)
+ .withSourceInformation(sourceInformation);
}
}
- void generateConstant(ConstantValue constant) {
+ void generateConstant(ConstantValue constant,
+ SourceInformation sourceInformation) {
if (constant.isFunction) {
FunctionConstantValue function = constant;
registry.registerStaticUse(function.element);
@@ -1869,12 +1911,13 @@
registry.registerTypeConstant(element);
}
}
- push(backend.emitter.constantReference(constant));
+ push(backend.emitter.constantReference(constant)
+ .withSourceInformation(sourceInformation));
}
visitConstant(HConstant node) {
assert(isGenerateAtUseSite(node));
- generateConstant(node.constant);
+ generateConstant(node.constant, node.sourceInformation);
registry.registerCompileTimeConstant(node.constant);
backend.constants.addCompileTimeConstantForEmission(node.constant);
@@ -1882,8 +1925,7 @@
visitNot(HNot node) {
assert(node.inputs.length == 1);
- generateNot(node.inputs[0]);
- attachLocationToLast(node);
+ generateNot(node.inputs[0], node.sourceInformation);
}
static String mapRelationalOperator(String op, bool inverse) {
@@ -1900,7 +1942,7 @@
return inverse ? inverseOperator[op] : op;
}
- void generateNot(HInstruction input) {
+ void generateNot(HInstruction input, SourceInformation sourceInformation) {
bool canGenerateOptimizedComparison(HInstruction instruction) {
if (instruction is !HRelational) return false;
@@ -1921,29 +1963,31 @@
if (isGenerateAtUseSite(input)) {
handledBySpecialCase = true;
if (input is HIs) {
- emitIs(input, '!==');
+ emitIs(input, '!==', sourceInformation);
} else if (input is HIsViaInterceptor) {
- emitIsViaInterceptor(input, true);
+ emitIsViaInterceptor(input, sourceInformation, negative: true);
} else if (input is HNot) {
use(input.inputs[0]);
} else if (input is HIdentity) {
- emitIdentityComparison(input, true);
+ emitIdentityComparison(input, sourceInformation, inverse: true);
} else if (input is HBoolify) {
use(input.inputs[0]);
- push(new js.Binary("!==", pop(), newLiteralBool(true)), input);
+ push(new js.Binary("!==", pop(),
+ newLiteralBool(true, input.sourceInformation))
+ .withSourceInformation(sourceInformation));
} else if (canGenerateOptimizedComparison(input)) {
HRelational relational = input;
BinaryOperation operation =
relational.operation(backend.constantSystem);
String op = mapRelationalOperator(operation.name, true);
- visitRelational(input, op);
+ handleInvokeBinary(input, op, sourceInformation);
} else {
handledBySpecialCase = false;
}
}
if (!handledBySpecialCase) {
use(input);
- push(new js.Prefix("!", pop()));
+ push(new js.Prefix("!", pop()).withSourceInformation(sourceInformation));
}
}
@@ -1974,7 +2018,7 @@
} else if (node.inputs[1].isConstantBoolean()) {
String operation = node.inputs[1].isConstantFalse() ? '&&' : '||';
if (operation == '||') {
- generateNot(input);
+ generateNot(input, input.sourceInformation);
} else {
use(input);
}
@@ -1995,10 +2039,12 @@
assert(node.inputs.length == 1);
HInstruction input = node.inputs[0];
if (input.isConstantNull()) {
- pushStatement(new js.Return(null), node);
+ pushStatement(new js.Return()
+ .withSourceInformation(node.sourceInformation));
} else {
use(node.inputs[0]);
- pushStatement(new js.Return(pop()), node);
+ pushStatement(new js.Return(pop())
+ .withSourceInformation(node.sourceInformation));
}
}
@@ -2009,20 +2055,24 @@
visitThrow(HThrow node) {
if (node.isRethrow) {
use(node.inputs[0]);
- pushStatement(new js.Throw(pop()), node);
+ pushStatement(new js.Throw(pop())
+ .withSourceInformation(node.sourceInformation));
} else {
- generateThrowWithHelper('wrapException', node.inputs[0]);
+ generateThrowWithHelper('wrapException', node.inputs[0],
+ sourceInformation: node.sourceInformation);
}
}
visitAwait(HAwait node) {
use(node.inputs[0]);
- push(new js.Await(pop()), node);
+ push(new js.Await(pop())
+ .withSourceInformation(node.sourceInformation));
}
visitYield(HYield node) {
use(node.inputs[0]);
- pushStatement(new js.DartYield(pop(), node.hasStar), node);
+ pushStatement(new js.DartYield(pop(), node.hasStar)
+ .withSourceInformation(node.sourceInformation));
}
visitRangeConversion(HRangeConversion node) {
@@ -2072,13 +2122,15 @@
generateThrowWithHelper('ioore', [node.array, node.index]);
currentContainer = oldContainer;
thenBody = unwrapStatement(thenBody);
- pushStatement(new js.If.noElse(underOver, thenBody), node);
+ pushStatement(new js.If.noElse(underOver, thenBody)
+ .withSourceInformation(node.sourceInformation));
} else {
generateThrowWithHelper('ioore', [node.array, node.index]);
}
}
- void generateThrowWithHelper(String helperName, argument) {
+ void generateThrowWithHelper(String helperName, argument,
+ {SourceInformation sourceInformation}) {
Element helper = backend.findHelper(helperName);
registry.registerStaticUse(helper);
js.Expression jsHelper = backend.emitter.staticFunctionAccess(helper);
@@ -2095,22 +2147,25 @@
use(argument);
arguments.add(pop());
}
- js.Call value = new js.Call(jsHelper, arguments.toList(growable: false));
- value = attachLocation(value, location);
+ js.Call value = new js.Call(jsHelper, arguments.toList(growable: false),
+ sourceInformation: sourceInformation);
// BUG(4906): Using throw/return here adds to the size of the generated code
// but it has the advantage of explicitly telling the JS engine that
// this code path will terminate abruptly. Needs more work.
if (helperName == 'wrapException') {
- pushStatement(new js.Throw(value));
+ pushStatement(new js.Throw(value)
+ .withSourceInformation(sourceInformation));
} else {
Element element = work.element;
if (element is FunctionElement && element.asyncMarker.isYielding) {
// `return <expr>;` is illegal in a sync* or async* function.
// To have the the async-translator working, we avoid introducing
// `return` nodes.
- pushStatement(new js.ExpressionStatement(value));
+ pushStatement(new js.ExpressionStatement(value)
+ .withSourceInformation(sourceInformation));
} else {
- pushStatement(new js.Return(value));
+ pushStatement(new js.Return(value)
+ .withSourceInformation(sourceInformation));
}
}
}
@@ -2123,9 +2178,9 @@
registry.registerStaticUse(helper);
js.Expression jsHelper = backend.emitter.staticFunctionAccess(helper);
- js.Call value = new js.Call(jsHelper, [pop()]);
- value = attachLocation(value, argument);
- push(value, node);
+ js.Call value = new js.Call(jsHelper, [pop()])
+ .withSourceInformation(node.sourceInformation);
+ push(value);
}
void visitSwitch(HSwitch node) {
@@ -2136,10 +2191,12 @@
Element element = node.element;
assert(element.isFunction || element.isField);
if (element.isFunction) {
- push(backend.emitter.isolateStaticClosureAccess(element));
+ push(backend.emitter.isolateStaticClosureAccess(element)
+ .withSourceInformation(node.sourceInformation));
registry.registerGetOfStaticFunction(element);
} else {
- push(backend.emitter.staticFieldAccess(element));
+ push(backend.emitter.staticFieldAccess(element)
+ .withSourceInformation(node.sourceInformation));
}
registry.registerStaticUse(element);
}
@@ -2149,22 +2206,25 @@
registry.registerStaticUse(element);
js.Expression lazyGetter =
backend.emitter.isolateLazyInitializerAccess(element);
- js.Call call = new js.Call(lazyGetter, <js.Expression>[]);
- push(call, node);
+ js.Call call = new js.Call(lazyGetter, <js.Expression>[],
+ sourceInformation: node.sourceInformation);
+ push(call);
}
void visitStaticStore(HStaticStore node) {
registry.registerStaticUse(node.element);
js.Node variable = backend.emitter.staticFieldAccess(node.element);
use(node.inputs[0]);
- push(new js.Assignment(variable, pop()), node);
+ push(new js.Assignment(variable, pop())
+ .withSourceInformation(node.sourceInformation));
}
void visitStringConcat(HStringConcat node) {
use(node.left);
js.Expression jsLeft = pop();
use(node.right);
- push(new js.Binary('+', jsLeft, pop()), node);
+ push(new js.Binary('+', jsLeft, pop())
+ .withSourceInformation(node.sourceInformation));
}
void visitStringify(HStringify node) {
@@ -2181,7 +2241,8 @@
// The context is already <string> + value.
} else {
// Force an empty string for the first operand.
- push(new js.Binary('+', js.string(""), pop()), node);
+ push(new js.Binary('+', js.string(""), pop())
+ .withSourceInformation(node.sourceInformation));
}
} else {
Element convertToString = backend.getStringInterpolationHelper();
@@ -2189,7 +2250,8 @@
js.Expression jsHelper =
backend.emitter.staticFunctionAccess(convertToString);
use(input);
- push(new js.Call(jsHelper, <js.Expression>[pop()]), node);
+ push(new js.Call(jsHelper, <js.Expression>[pop()],
+ sourceInformation: node.sourceInformation));
}
}
@@ -2203,14 +2265,16 @@
use(input);
return pop();
}).toList();
- push(new js.ArrayInitializer(elements), node);
+ push(new js.ArrayInitializer(elements)
+ .withSourceInformation(node.sourceInformation));
}
void visitIndex(HIndex node) {
use(node.receiver);
js.Expression receiver = pop();
use(node.index);
- push(new js.PropertyAccess(receiver, pop()), node);
+ push(new js.PropertyAccess(receiver, pop())
+ .withSourceInformation(node.sourceInformation));
}
void visitIndexAssign(HIndexAssign node) {
@@ -2219,8 +2283,8 @@
use(node.index);
js.Expression index = pop();
use(node.value);
- push(new js.Assignment(new js.PropertyAccess(receiver, index), pop()),
- node);
+ push(new js.Assignment(new js.PropertyAccess(receiver, index), pop())
+ .withSourceInformation(node.sourceInformation));
}
void checkInt(HInstruction input, String cmp) {
@@ -2231,47 +2295,62 @@
push(new js.Binary(cmp, left, or0));
}
- void checkBigInt(HInstruction input, String cmp) {
+ void checkBigInt(HInstruction input, String cmp,
+ SourceInformation sourceInformation) {
use(input);
js.Expression left = pop();
use(input);
js.Expression right = pop();
// TODO(4984): Deal with infinity and -0.0.
- push(js.js('Math.floor(#) $cmp #', <js.Expression>[left, right]));
+ push(js.js('Math.floor(#) $cmp #', <js.Expression>[left, right])
+ .withSourceInformation(sourceInformation));
}
- void checkTypeOf(HInstruction input, String cmp, String typeName) {
+ void checkTypeOf(HInstruction input, String cmp, String typeName,
+ SourceInformation sourceInformation) {
use(input);
js.Expression typeOf = new js.Prefix("typeof", pop());
push(new js.Binary(cmp, typeOf, js.string(typeName)));
}
- void checkNum(HInstruction input, String cmp)
- => checkTypeOf(input, cmp, 'number');
+ void checkNum(HInstruction input, String cmp,
+ SourceInformation sourceInformation) {
+ return checkTypeOf(input, cmp, 'number', sourceInformation);
+ }
- void checkDouble(HInstruction input, String cmp) => checkNum(input, cmp);
+ void checkDouble(HInstruction input, String cmp,
+ SourceInformation sourceInformation) {
+ return checkNum(input, cmp, sourceInformation);
+ }
- void checkString(HInstruction input, String cmp)
- => checkTypeOf(input, cmp, 'string');
+ void checkString(HInstruction input, String cmp,
+ SourceInformation sourceInformation) {
+ return checkTypeOf(input, cmp, 'string', sourceInformation);
+ }
- void checkBool(HInstruction input, String cmp)
- => checkTypeOf(input, cmp, 'boolean');
+ void checkBool(HInstruction input, String cmp,
+ SourceInformation sourceInformation) {
+ return checkTypeOf(input, cmp, 'boolean', sourceInformation);
+ }
- void checkObject(HInstruction input, String cmp) {
+ void checkObject(HInstruction input, String cmp,
+ SourceInformation sourceInformation) {
assert(NullConstantValue.JsNull == 'null');
if (cmp == "===") {
- checkTypeOf(input, '===', 'object');
+ checkTypeOf(input, '===', 'object', sourceInformation);
js.Expression left = pop();
use(input);
js.Expression notNull = new js.Binary("!==", pop(), new js.LiteralNull());
- push(new js.Binary("&&", left, notNull));
+ push(new js.Binary("&&", left, notNull)
+ .withSourceInformation(sourceInformation));
} else {
assert(cmp == "!==");
- checkTypeOf(input, '!==', 'object');
+ checkTypeOf(input, '!==', 'object', sourceInformation);
js.Expression left = pop();
use(input);
js.Expression eqNull = new js.Binary("===", pop(), new js.LiteralNull());
- push(new js.Binary("||", left, eqNull));
+ push(new js.Binary("||", left, eqNull)
+ .withSourceInformation(sourceInformation));
}
}
@@ -2322,7 +2401,9 @@
}
void checkType(HInstruction input, HInstruction interceptor,
- DartType type, {bool negative: false}) {
+ DartType type,
+ SourceInformation sourceInformation,
+ {bool negative: false}) {
Element element = type.element;
if (element == backend.jsArrayClass) {
checkArray(input, negative ? '!==': '===');
@@ -2357,73 +2438,92 @@
return;
}
if (interceptor != null) {
- checkTypeViaProperty(interceptor, type, negative);
+ checkTypeViaProperty(interceptor, type, sourceInformation,
+ negative: negative);
} else {
- checkTypeViaProperty(input, type, negative);
+ checkTypeViaProperty(input, type, sourceInformation, negative: negative);
}
}
- void checkTypeViaProperty(HInstruction input, DartType type, bool negative) {
+ void checkTypeViaProperty(HInstruction input, DartType type,
+ SourceInformation sourceInformation,
+ {bool negative: false}) {
registry.registerIsCheck(type);
use(input);
js.PropertyAccess field =
- new js.PropertyAccess(pop(), backend.namer.operatorIsType(type));
+ new js.PropertyAccess(pop(), backend.namer.operatorIsType(type))
+ .withSourceInformation(sourceInformation);
// We always negate at least once so that the result is boolified.
- push(new js.Prefix('!', field));
+ push(new js.Prefix('!', field)
+ .withSourceInformation(sourceInformation));
// If the result is not negated, put another '!' in front.
- if (!negative) push(new js.Prefix('!', pop()));
+ if (!negative) {
+ push(new js.Prefix('!', pop())
+ .withSourceInformation(sourceInformation));
+ }
}
void checkTypeViaInstanceof(
- HInstruction input, DartType type, bool negative) {
+ HInstruction input, DartType type,
+ SourceInformation sourceInformation,
+ {bool negative: false}) {
registry.registerIsCheck(type);
use(input);
js.Expression jsClassReference =
backend.emitter.constructorAccess(type.element);
- push(js.js('# instanceof #', [pop(), jsClassReference]));
- if (negative) push(new js.Prefix('!', pop()));
+ push(js.js('# instanceof #', [pop(), jsClassReference])
+ .withSourceInformation(sourceInformation));
+ if (negative) {
+ push(new js.Prefix('!', pop())
+ .withSourceInformation(sourceInformation));
+ }
registry.registerInstantiatedType(type);
}
void handleNumberOrStringSupertypeCheck(HInstruction input,
HInstruction interceptor,
DartType type,
- { bool negative: false }) {
- assert(!identical(type.element, compiler.listClass)
- && !Elements.isListSupertype(type.element, compiler)
- && !Elements.isStringOnlySupertype(type.element, compiler));
+ SourceInformation sourceInformation,
+ {bool negative: false}) {
+ assert(!identical(type.element, compiler.listClass) &&
+ !Elements.isListSupertype(type.element, compiler) &&
+ !Elements.isStringOnlySupertype(type.element, compiler));
String relation = negative ? '!==' : '===';
- checkNum(input, relation);
+ checkNum(input, relation, sourceInformation);
js.Expression numberTest = pop();
- checkString(input, relation);
+ checkString(input, relation, sourceInformation);
js.Expression stringTest = pop();
- checkObject(input, relation);
+ checkObject(input, relation, sourceInformation);
js.Expression objectTest = pop();
- checkType(input, interceptor, type, negative: negative);
+ checkType(input, interceptor, type, sourceInformation, negative: negative);
String combiner = negative ? '&&' : '||';
String combiner2 = negative ? '||' : '&&';
push(new js.Binary(combiner,
- new js.Binary(combiner, numberTest, stringTest),
- new js.Binary(combiner2, objectTest, pop())));
+ new js.Binary(combiner, numberTest, stringTest)
+ .withSourceInformation(sourceInformation),
+ new js.Binary(combiner2, objectTest, pop())
+ .withSourceInformation(sourceInformation))
+ .withSourceInformation(sourceInformation));
}
void handleStringSupertypeCheck(HInstruction input,
HInstruction interceptor,
DartType type,
- { bool negative: false }) {
+ SourceInformation sourceInformation,
+ {bool negative: false}) {
assert(!identical(type.element, compiler.listClass)
&& !Elements.isListSupertype(type.element, compiler)
&& !Elements.isNumberOrStringSupertype(type.element, compiler));
String relation = negative ? '!==' : '===';
- checkString(input, relation);
+ checkString(input, relation, sourceInformation);
js.Expression stringTest = pop();
- checkObject(input, relation);
+ checkObject(input, relation, sourceInformation);
js.Expression objectTest = pop();
- checkType(input, interceptor, type, negative: negative);
+ checkType(input, interceptor, type, sourceInformation, negative: negative);
String combiner = negative ? '||' : '&&';
push(new js.Binary(negative ? '&&' : '||',
stringTest,
@@ -2433,31 +2533,33 @@
void handleListOrSupertypeCheck(HInstruction input,
HInstruction interceptor,
DartType type,
+ SourceInformation sourceInformation,
{ bool negative: false }) {
assert(!identical(type.element, compiler.stringClass)
&& !Elements.isStringOnlySupertype(type.element, compiler)
&& !Elements.isNumberOrStringSupertype(type.element, compiler));
String relation = negative ? '!==' : '===';
- checkObject(input, relation);
+ checkObject(input, relation, sourceInformation);
js.Expression objectTest = pop();
checkArray(input, relation);
js.Expression arrayTest = pop();
- checkType(input, interceptor, type, negative: negative);
+ checkType(input, interceptor, type, sourceInformation, negative: negative);
String combiner = negative ? '&&' : '||';
push(new js.Binary(negative ? '||' : '&&',
objectTest,
- new js.Binary(combiner, arrayTest, pop())));
+ new js.Binary(combiner, arrayTest, pop()))
+ .withSourceInformation(sourceInformation));
}
void visitIs(HIs node) {
- emitIs(node, "===");
+ emitIs(node, "===", node.sourceInformation);
}
void visitIsViaInterceptor(HIsViaInterceptor node) {
- emitIsViaInterceptor(node, false);
+ emitIsViaInterceptor(node, node.sourceInformation, negative: false);
}
- void emitIs(HIs node, String relation) {
+ void emitIs(HIs node, String relation, SourceInformation sourceInformation) {
DartType type = node.typeExpression;
registry.registerIsCheck(type);
HInstruction input = node.expression;
@@ -2484,64 +2586,73 @@
} else if (identical(element, objectClass) || type.treatAsDynamic) {
// The constant folder also does this optimization, but we make
// it safe by assuming it may have not run.
- push(newLiteralBool(!negative), node);
+ push(newLiteralBool(!negative, sourceInformation));
} else if (element == compiler.stringClass) {
- checkString(input, relation);
- attachLocationToLast(node);
+ checkString(input, relation, sourceInformation);
} else if (element == compiler.doubleClass) {
- checkDouble(input, relation);
- attachLocationToLast(node);
+ checkDouble(input, relation, sourceInformation);
} else if (element == compiler.numClass) {
- checkNum(input, relation);
- attachLocationToLast(node);
+ checkNum(input, relation, sourceInformation);
} else if (element == compiler.boolClass) {
- checkBool(input, relation);
- attachLocationToLast(node);
+ checkBool(input, relation, sourceInformation);
} else if (element == compiler.intClass) {
// The is check in the code tells us that it might not be an
// int. So we do a typeof first to avoid possible
// deoptimizations on the JS engine due to the Math.floor check.
- checkNum(input, relation);
+ checkNum(input, relation, sourceInformation);
js.Expression numTest = pop();
- checkBigInt(input, relation);
- push(new js.Binary(negative ? '||' : '&&', numTest, pop()), node);
+ checkBigInt(input, relation, sourceInformation);
+ push(new js.Binary(negative ? '||' : '&&', numTest, pop())
+ .withSourceInformation(sourceInformation));
} else if (node.useInstanceOf) {
assert(interceptor == null);
- checkTypeViaInstanceof(input, type, negative);
- attachLocationToLast(node);
+ checkTypeViaInstanceof(input, type,
+ sourceInformation,
+ negative: negative);
} else if (Elements.isNumberOrStringSupertype(element, compiler)) {
handleNumberOrStringSupertypeCheck(
- input, interceptor, type, negative: negative);
- attachLocationToLast(node);
+ input, interceptor, type,
+ sourceInformation,
+ negative: negative);
} else if (Elements.isStringOnlySupertype(element, compiler)) {
handleStringSupertypeCheck(
- input, interceptor, type, negative: negative);
- attachLocationToLast(node);
- } else if (identical(element, compiler.listClass)
- || Elements.isListSupertype(element, compiler)) {
+ input, interceptor, type,
+ sourceInformation,
+ negative: negative);
+ } else if (identical(element, compiler.listClass) ||
+ Elements.isListSupertype(element, compiler)) {
handleListOrSupertypeCheck(
- input, interceptor, type, negative: negative);
- attachLocationToLast(node);
+ input, interceptor, type,
+ sourceInformation,
+ negative: negative);
} else if (type.isFunctionType) {
- checkType(input, interceptor, type, negative: negative);
- attachLocationToLast(node);
+ checkType(input, interceptor, type,
+ sourceInformation,
+ negative: negative);
} else if ((input.canBePrimitive(compiler)
&& !input.canBePrimitiveArray(compiler))
|| input.canBeNull()) {
- checkObject(input, relation);
+ checkObject(input, relation, node.sourceInformation);
js.Expression objectTest = pop();
- checkType(input, interceptor, type, negative: negative);
- push(new js.Binary(negative ? '||' : '&&', objectTest, pop()), node);
+ checkType(input, interceptor, type,
+ sourceInformation,
+ negative: negative);
+ push(new js.Binary(negative ? '||' : '&&', objectTest, pop())
+ .withSourceInformation(sourceInformation));
} else {
- checkType(input, interceptor, type, negative: negative);
- attachLocationToLast(node);
+ checkType(input, interceptor, type,
+ sourceInformation,
+ negative: negative);
}
}
}
- void emitIsViaInterceptor(HIsViaInterceptor node, bool negative) {
- checkTypeViaProperty(node.interceptor, node.typeExpression, negative);
- attachLocationToLast(node);
+ void emitIsViaInterceptor(HIsViaInterceptor node,
+ SourceInformation sourceInformation,
+ {bool negative: false}) {
+ checkTypeViaProperty(node.interceptor, node.typeExpression,
+ sourceInformation,
+ negative: negative);
}
js.Expression generateReceiverOrArgumentTypeTest(
@@ -2561,22 +2672,23 @@
if (turnIntoNullCheck) {
use(input);
- return new js.Binary("==", pop(), new js.LiteralNull());
+ return new js.Binary("==", pop(), new js.LiteralNull())
+ .withSourceInformation(input.sourceInformation);
} else if (isIntCheck && !turnIntoNumCheck) {
// input is !int
- checkBigInt(input, '!==');
+ checkBigInt(input, '!==', input.sourceInformation);
return pop();
} else if (turnIntoNumCheck || checkedType.containsOnlyNum(classWorld)) {
// input is !num
- checkNum(input, '!==');
+ checkNum(input, '!==', input.sourceInformation);
return pop();
} else if (checkedType.containsOnlyBool(classWorld)) {
// input is !bool
- checkBool(input, '!==');
+ checkBool(input, '!==', input.sourceInformation);
return pop();
} else if (checkedType.containsOnlyString(classWorld)) {
// input is !string
- checkString(input, '!==');
+ checkString(input, '!==', input.sourceInformation);
return pop();
}
compiler.internalError(input, 'Unexpected check.');
@@ -2607,7 +2719,8 @@
}
currentContainer = oldContainer;
body = unwrapStatement(body);
- pushStatement(new js.If.noElse(test, body), node);
+ pushStatement(new js.If.noElse(test, body)
+ .withSourceInformation(node.sourceInformation));
return;
}
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 765ce05..0adc6e3 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -172,11 +172,14 @@
return result;
}
- HConstant addConstant(ConstantValue constant, Compiler compiler) {
+ HConstant addConstant(ConstantValue constant, Compiler compiler,
+ {SourceInformation sourceInformation}) {
HConstant result = constants[constant];
+ // TODO(johnniwinther): Support source information per constant reference.
if (result == null) {
TypeMask type = computeTypeMask(compiler, constant);
- result = new HConstant.internal(constant, type);
+ result = new HConstant.internal(constant, type)
+ ..sourceInformation = sourceInformation;
entry.addAtExit(result);
constants[constant] = result;
} else if (result.block == null) {
@@ -187,12 +190,13 @@
}
HConstant addDeferredConstant(ConstantValue constant, PrefixElement prefix,
+ SourceInformation sourceInformation,
Compiler compiler) {
// TODO(sigurdm,johnniwinter): These deferred constants should be created
// by the constant evaluator.
ConstantValue wrapper = new DeferredConstantValue(constant, prefix);
compiler.deferredLoadTask.registerConstantDeferredUse(wrapper, prefix);
- return addConstant(wrapper, compiler);
+ return addConstant(wrapper, compiler, sourceInformation: sourceInformation);
}
HConstant addConstantInt(int i, Compiler compiler) {
@@ -1316,6 +1320,7 @@
HBoolify(HInstruction value, TypeMask type)
: super(<HInstruction>[value], type) {
setUseGvn();
+ sourceInformation = value.sourceInformation;
}
accept(HVisitor visitor) => visitor.visitBoolify(this);
@@ -1528,8 +1533,11 @@
this.selector,
inputs,
type,
+ SourceInformation sourceInformation,
{this.isSetter})
- : super(element, inputs, type);
+ : super(element, inputs, type) {
+ this.sourceInformation = sourceInformation;
+ }
HInstruction get receiver => inputs[0];
HInstruction getDartReceiver(Compiler compiler) {
@@ -2302,14 +2310,19 @@
}
class HReturn extends HControlFlow {
- HReturn(value) : super(<HInstruction>[value]);
+ HReturn(HInstruction value, SourceInformation sourceInformation)
+ : super(<HInstruction>[value]) {
+ this.sourceInformation = sourceInformation;
+ }
toString() => 'return';
accept(HVisitor visitor) => visitor.visitReturn(this);
}
class HThrowExpression extends HInstruction {
- HThrowExpression(value)
- : super(<HInstruction>[value], const TypeMask.nonNullEmpty());
+ HThrowExpression(HInstruction value, SourceInformation sourceInformation)
+ : super(<HInstruction>[value], const TypeMask.nonNullEmpty()) {
+ this.sourceInformation = sourceInformation;
+ }
toString() => 'throw expression';
accept(HVisitor visitor) => visitor.visitThrowExpression(this);
bool canThrow() => true;
@@ -2337,7 +2350,12 @@
class HThrow extends HControlFlow {
final bool isRethrow;
- HThrow(value, {this.isRethrow: false}) : super(<HInstruction>[value]);
+ HThrow(HInstruction value,
+ SourceInformation sourceInformation,
+ {this.isRethrow: false})
+ : super(<HInstruction>[value]) {
+ this.sourceInformation = sourceInformation;
+ }
toString() => 'throw';
accept(HVisitor visitor) => visitor.visitThrow(this);
}
diff --git a/pkg/compiler/lib/src/tree/prettyprint.dart b/pkg/compiler/lib/src/tree/prettyprint.dart
index f01963b..ab4c87c 100644
--- a/pkg/compiler/lib/src/tree/prettyprint.dart
+++ b/pkg/compiler/lib/src/tree/prettyprint.dart
@@ -10,100 +10,18 @@
* TODO(smok): Add main() to run from command-line to print out tree for given
* .dart file.
*/
-class PrettyPrinter extends Indentation implements Visitor {
-
- StringBuffer sb;
- Link<String> tagStack;
-
- PrettyPrinter() :
- sb = new StringBuffer(),
- tagStack = const Link<String>();
-
- void pushTag(String tag) {
- tagStack = tagStack.prepend(tag);
- indentMore();
- }
-
- String popTag() {
- assert(!tagStack.isEmpty);
- String tag = tagStack.head;
- tagStack = tagStack.tail;
- indentLess();
- return tag;
- }
-
- /**
- * Adds given string to result string.
- */
- void add(String string) {
- sb.write(string);
- }
-
- void addBeginAndEndTokensToParams(Node node, Map params) {
+class PrettyPrinter extends Indentation with Tagging<Node> implements Visitor {
+ @override
+ void addDefaultParameters(Node node, Map params) {
params['getBeginToken'] = tokenToStringOrNull(node.getBeginToken());
params['getEndToken'] = tokenToStringOrNull(node.getEndToken());
}
- /**
- * Adds given node type to result string.
- * The method "opens" the node, meaning that all output after calling
- * this method and before calling closeNode() will represent contents
- * of given node.
- */
- void openNode(Node node, String type, [Map params]) {
- if (params == null) params = new Map();
- addCurrentIndent();
- sb.write("<");
- addBeginAndEndTokensToParams(node, params);
- addTypeWithParams(type, params);
- sb.write(">\n");
- pushTag(type);
- }
-
- /**
- * Adds given node to result string.
- */
- void openAndCloseNode(Node node, String type, [Map params]) {
- if (params == null) params = new Map();
- addCurrentIndent();
- sb.write("<");
- addBeginAndEndTokensToParams(node, params);
- addTypeWithParams(type, params);
- sb.write("/>\n");
- }
-
- /**
- * Closes current node type.
- */
- void closeNode() {
- String tag = popTag();
- addCurrentIndent();
- sb.write("</");
- addTypeWithParams(tag);
- sb.write(">\n");
- }
-
- void addTypeWithParams(String type, [Map params]) {
- if (params == null) params = new Map();
- sb.write("${type}");
- params.forEach((k, v) {
- String value;
- if (v != null) {
- var str = v;
- if (v is Token) str = v.value;
- value = str
- .replaceAll("<", "<")
- .replaceAll(">", ">")
- .replaceAll('"', "'");
- } else {
- value = "[null]";
- }
- sb.write(' $k="$value"');
- });
- }
-
- void addCurrentIndent() {
- sb.write(indentation);
+ String valueToString(var value) {
+ if (value is Token) {
+ return value.value;
+ }
+ return value;
}
/**
diff --git a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
index bad7690..be59dc9 100644
--- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
+++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
@@ -8,7 +8,35 @@
import '../tree_ir_nodes.dart';
/**
- * Performs the following transformations on the tree:
+ * Translates to direct-style.
+ *
+ * In addition to the general IR constraints (see [CheckTreeIntegrity]),
+ * the input is assumed to satisfy the following criteria:
+ *
+ * All expressions other than those nested in [Assign] or [ExpressionStatement]
+ * must be simple. A [VariableUse] and [This] is a simple expression.
+ * The right-hand of an [Assign] may not be an [Assign].
+ *
+ * Moreover, every variable must either be an SSA variable or a mutable
+ * variable, and must satisfy the corresponding criteria:
+ *
+ * SSA VARIABLE:
+ * An SSA variable must have a unique definition site, which is either an
+ * assignment or label. In case of a label, its target must act as the unique
+ * reaching definition of that variable at all uses of the variable and at
+ * all other label targets where the variable is in scope.
+ *
+ * (The second criterion is to ensure that we can move a use of an SSA variable
+ * across a label without changing its reaching definition).
+ *
+ * MUTABLE VARIABLE:
+ * Uses of mutable variables are considered complex expressions, and hence must
+ * not be nested in other expressions. Assignments to mutable variables must
+ * have simple right-hand sides.
+ *
+ * ----
+ *
+ * This pass performs the following transformations on the tree:
* - Assignment inlining
* - Assignment expression propagation
* - If-to-conditional conversion
@@ -113,7 +141,9 @@
@override
void rewrite(FunctionDefinition node) {
+ node.parameters.forEach(pushDominatingAssignment);
node.body = visitStatement(node.body);
+ node.parameters.forEach(popDominatingAssignment);
}
/// The most recently evaluated impure expressions, with the most recent
@@ -145,6 +175,14 @@
/// traversal, the first use is the last one seen).
Map<Variable, int> unseenUses = <Variable, int>{};
+ /// Number of assignments to a given variable that dominate the current
+ /// position.
+ ///
+ /// Pure expressions will not be inlined if it uses a variable with more than
+ /// one dominating assignment, because the reaching definition of the used
+ /// variable might have changed since it was put in the environment.
+ final Map<Variable, int> dominatingAssignments = <Variable, int>{};
+
/// Rewriter for methods.
StatementRewriter() : constantEnvironment = <Variable, Expression>{};
@@ -196,6 +234,31 @@
return value is VariableUse ? value.variable : null;
}
+ /// True if the given expression (taken from [constantEnvironment]) uses a
+ /// variable that might have been reassigned since [node] was evaluated.
+ bool hasUnsafeVariableUse(Expression node) {
+ bool wasFound = false;
+ VariableUseVisitor.visit(node, (VariableUse use) {
+ if (dominatingAssignments[use.variable] > 1) {
+ wasFound = true;
+ }
+ });
+ return wasFound;
+ }
+
+ void pushDominatingAssignment(Variable variable) {
+ if (variable != null) {
+ dominatingAssignments.putIfAbsent(variable, () => 0);
+ ++dominatingAssignments[variable];
+ }
+ }
+
+ void popDominatingAssignment(Variable variable) {
+ if (variable != null) {
+ --dominatingAssignments[variable];
+ }
+ }
+
@override
Expression visitVariableUse(VariableUse node) {
// Count of number of unseen uses remaining.
@@ -210,7 +273,7 @@
// Propagate constant to use site.
Expression constant = constantEnvironment[node.variable];
- if (constant != null) {
+ if (constant != null && !hasUnsafeVariableUse(constant)) {
--node.variable.readCount;
return visitExpression(constant);
}
@@ -287,7 +350,6 @@
return exp is Constant ||
exp is This ||
exp is CreateInvocationMirror ||
- exp is InvokeStatic && exp.isEffectivelyConstant ||
exp is Interceptor ||
exp is ApplyBuiltinOperator ||
exp is VariableUse && constantEnvironment.containsKey(exp.variable);
@@ -301,6 +363,8 @@
}
Statement visitExpressionStatement(ExpressionStatement stmt) {
+ Variable leftHand = getLeftHand(stmt.expression);
+ pushDominatingAssignment(leftHand);
if (isEffectivelyConstantAssignment(stmt.expression) &&
!usesRecentlyAssignedVariable(stmt.expression)) {
Assign assign = stmt.expression;
@@ -308,15 +372,29 @@
// They are always safe to propagate (though we should avoid duplication).
// Moreover, they should not prevent other expressions from propagating.
if (assign.variable.readCount == 1) {
- // A single-use constant should always be propagted to its use site.
+ // A single-use constant should always be propagated to its use site.
constantEnvironment[assign.variable] = assign.value;
- --assign.variable.writeCount;
- return visitStatement(stmt.next);
+ Statement next = visitStatement(stmt.next);
+ popDominatingAssignment(leftHand);
+ if (assign.variable.readCount > 0) {
+ // The assignment could not be propagated into the successor, either
+ // because it has an unsafe variable use (see [hasUnsafeVariableUse])
+ // or because the use is outside the current try block, and we do
+ // not currently support constant propagation out of a try block.
+ constantEnvironment.remove(assign.variable);
+ assign.value = visitExpression(assign.value);
+ stmt.next = next;
+ return stmt;
+ } else {
+ --assign.variable.writeCount;
+ return next;
+ }
} else {
// With more than one use, we cannot propagate the constant.
// Visit the following statement without polluting [environment] so
// that any preceding non-constant assignments might still propagate.
stmt.next = visitStatement(stmt.next);
+ popDominatingAssignment(leftHand);
assign.value = visitExpression(assign.value);
return stmt;
}
@@ -325,6 +403,7 @@
// until this has propagated.
environment.add(stmt.expression);
stmt.next = visitStatement(stmt.next);
+ popDominatingAssignment(leftHand);
if (!environment.isEmpty && environment.last == stmt.expression) {
// Retain the expression statement.
environment.removeLast();
@@ -549,7 +628,9 @@
safeForInlining = new Set<Label>();
node.tryBody = visitStatement(node.tryBody);
safeForInlining = saved;
+ node.catchParameters.forEach(pushDominatingAssignment);
node.catchBody = visitStatement(node.catchBody);
+ node.catchParameters.forEach(popDominatingAssignment);
});
return node;
}
@@ -755,7 +836,11 @@
// We are not in risk of reprocessing the original subexpressions because
// the combined expression will always hide them inside a Conditional.
environment.add(values.combined);
+
+ Variable leftHand = getLeftHand(values.combined);
+ pushDominatingAssignment(leftHand);
Statement next = combineStatements(s.next, t.next);
+ popDominatingAssignment(leftHand);
if (next == null) {
// Statements could not be combined.
@@ -830,7 +915,10 @@
combineExpressions(s.expression, t.expression);
if (values == null) return null;
environment.add(values.combined);
+ Variable leftHand = getLeftHand(values.combined);
+ pushDominatingAssignment(leftHand);
Statement next = combineStatements(s.next, t.next);
+ popDominatingAssignment(leftHand);
if (next == null) {
// The successors could not be combined.
// Restore the environment and uncombine the values again.
@@ -1046,3 +1134,19 @@
visitInnerFunction(FunctionDefinition node) {}
}
+
+typedef VariableUseCallback(VariableUse use);
+
+class VariableUseVisitor extends RecursiveVisitor {
+ VariableUseCallback callback;
+
+ VariableUseVisitor(this.callback);
+
+ visitVariableUse(VariableUse use) => callback(use);
+
+ visitInnerFunction(FunctionDefinition node) {}
+
+ static void visit(Expression node, VariableUseCallback callback) {
+ new VariableUseVisitor(callback).visitExpression(node);
+ }
+}
diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
index 6e39aae..6ca4430 100644
--- a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
+++ b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
@@ -54,15 +54,6 @@
// is the mapping from continuations to labels.
final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{};
- /// A stack of singly-used labels that can be safely inlined at their use
- /// site.
- ///
- /// Code for continuations with exactly one use is inlined at the use site.
- /// This is not safe if the code is moved inside the scope of an exception
- /// handler (i.e., into a try block). We keep a stack of singly-referenced
- /// continuations that are in scope without crossing a binding for a handler.
- List<cps_ir.Continuation> safeForInlining = <cps_ir.Continuation>[];
-
ExecutableElement currentElement;
/// The 'this' Parameter for currentElement or the enclosing method.
cps_ir.Parameter thisParameter;
@@ -318,16 +309,12 @@
Statement visitLetCont(cps_ir.LetCont node) {
// Introduce labels for continuations that need them.
- int safeForInliningLengthOnEntry = safeForInlining.length;
for (cps_ir.Continuation continuation in node.continuations) {
if (continuation.hasMultipleUses || continuation.isRecursive) {
labels[continuation] = new Label();
- } else {
- safeForInlining.add(continuation);
}
}
Statement body = visit(node.body);
- safeForInlining.length = safeForInliningLengthOnEntry;
// Continuations are bound at the same level, but they have to be
// translated as if nested. This is because the body can invoke any
// of them from anywhere, so it must be nested inside all of them.
@@ -352,10 +339,7 @@
}
Statement visitLetHandler(cps_ir.LetHandler node) {
- List<cps_ir.Continuation> saved = safeForInlining;
- safeForInlining = <cps_ir.Continuation>[];
Statement tryBody = visit(node.body);
- safeForInlining = saved;
List<Variable> catchParameters =
node.handler.parameters.map(getVariable).toList();
Statement catchBody = visit(node.handler.body);
@@ -366,7 +350,7 @@
// Calls are translated to direct style.
List<Expression> arguments = translateArguments(node.arguments);
Expression invoke = new InvokeStatic(node.target, node.selector, arguments,
- sourceInformation: node.sourceInformation);
+ node.sourceInformation);
return continueWithExpression(node.continuation, invoke);
}
@@ -490,7 +474,7 @@
: new WhileTrue(labels[cont], visit(cont.body));
} else {
if (cont.hasExactlyOneUse) {
- if (safeForInlining.contains(cont)) {
+ if (!node.isEscapingTry) {
return visit(cont.body);
}
labels[cont] = new Label();
diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart
index ab549e2..b30c368 100644
--- a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart
+++ b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart
@@ -181,16 +181,8 @@
final Selector selector;
final SourceInformation sourceInformation;
- /// True if the [target] is known not to diverge or read or write any
- /// mutable state.
- ///
- /// This is set for calls to `getInterceptor` and `identical` to indicate
- /// that they can be safely be moved across an impure expression
- /// (assuming the [arguments] are not affected by the impure expression).
- bool isEffectivelyConstant = false;
-
InvokeStatic(this.target, this.selector, this.arguments,
- {this.sourceInformation});
+ [this.sourceInformation]);
accept(ExpressionVisitor visitor) => visitor.visitInvokeStatic(this);
accept1(ExpressionVisitor1 visitor, arg) {
diff --git a/pkg/compiler/lib/src/use_unused_api.dart b/pkg/compiler/lib/src/use_unused_api.dart
index 215bfa5..3e90be4 100644
--- a/pkg/compiler/lib/src/use_unused_api.dart
+++ b/pkg/compiler/lib/src/use_unused_api.dart
@@ -26,13 +26,14 @@
import 'filenames.dart' as filenames;
import 'inferrer/concrete_types_inferrer.dart' as concrete_types_inferrer;
import 'inferrer/type_graph_inferrer.dart' as type_graph_inferrer;
-import 'io/code_output.dart' as io;
+import 'io/line_column_provider.dart' as io;
import 'io/source_map_builder.dart' as io;
import 'js/js.dart' as js;
import 'js_backend/js_backend.dart' as js_backend;
import 'js_emitter/js_emitter.dart' as js_emitter;
-import 'js_emitter/program_builder.dart' as program_builder;
+import 'js_emitter/program_builder/program_builder.dart' as program_builder;
import 'resolution/semantic_visitor.dart' as semantic_visitor;
+import 'resolution/operators.dart' as operators;
import 'source_file_provider.dart' as source_file_provider;
import 'ssa/ssa.dart' as ssa;
import 'tree/tree.dart' as tree;
@@ -50,7 +51,7 @@
useApi(null);
dart2js.main(arguments);
dart2jslib.isPublicName(null);
- useConstant(null, null, null, null, null);
+ useConstant();
useNode(null);
useUtil(null);
useSetlet(null);
@@ -65,7 +66,7 @@
useColor();
useFilenames();
useSsa(null);
- useIo(null, null);
+ useIo();
usedByTests();
useElements();
useIr(null);
@@ -81,16 +82,38 @@
useApi(api.ReadStringFromUri uri) {
}
-void useConstant(constants.ConstantValue constant,
- constants.ConstantExpression expression,
- constants.ConstructedConstantExpression constructedConstant,
- constants.ConstantSystem cs,
- constants.Environment env) {
+class NullConstantConstructorVisitor extends constants.ConstantConstructorVisitor {
+ @override
+ visitGenerative(constants.GenerativeConstantConstructor constructor, arg) {
+ }
+
+ @override
+ visitRedirectingFactory(
+ constants.RedirectingFactoryConstantConstructor constructor, arg) {
+ }
+
+ @override
+ visitRedirectingGenerative(
+ constants.RedirectingGenerativeConstantConstructor constructor, arg) {
+ }
+}
+
+void useConstant([constants.ConstantValue constant,
+ constants.ConstantExpression expression,
+ constants.ConstructedConstantExpression constructedConstant,
+ constants.ConstantSystem cs,
+ constants.Environment env]) {
constant.isObject;
cs.isBool(constant);
constructedConstant.computeInstanceType();
constructedConstant.computeInstanceFields();
expression.evaluate(null, null);
+ new NullConstantConstructorVisitor()
+ ..visit(null, null)
+ ..visitGenerative(null, null)
+ ..visitRedirectingFactory(null, null)
+ ..visitRedirectingGenerative(null, null);
+
}
void useNode(tree.Node node) {
@@ -217,11 +240,13 @@
new ssa.HStatementSequenceInformation(null);
}
-useIo(io.CodeBuffer buffer, io.LineColumnMap map) {
+useIo([io.LineColumnMap map,
+ io.LineColumnProvider provider]) {
map..addFirst(null, null, null)
..forEachLine(null)
..getFirstElementsInLine(null)
..forEachColumn(null, null);
+ provider.getOffset(null, null);
}
usedByTests() {
@@ -232,6 +257,7 @@
compiler.currentlyInUserCode();
type_graph_inferrer.TypeGraphInferrer typeGraphInferrer = null;
source_file_provider.SourceFileProvider sourceFileProvider = null;
+ sourceFileProvider.getSourceFile(null);
world.hasAnyUserDefinedGetter(null, null);
typeGraphInferrer.getCallersOf(null);
dart_types.Types.sorted(null);
@@ -290,6 +316,8 @@
}
useSemanticVisitor() {
+ operators.UnaryOperator.fromKind(null);
+ operators.BinaryOperator.fromKind(null);
new semantic_visitor.BulkSendVisitor()
..apply(null, null)
..visitSuperFieldFieldCompound(null, null, null, null, null, null);
diff --git a/pkg/compiler/lib/src/util/indentation.dart b/pkg/compiler/lib/src/util/indentation.dart
index 6206bd6..ddbeafa 100644
--- a/pkg/compiler/lib/src/util/indentation.dart
+++ b/pkg/compiler/lib/src/util/indentation.dart
@@ -51,3 +51,96 @@
return result;
}
}
+
+abstract class Tagging<N> implements Indentation {
+
+ StringBuffer sb = new StringBuffer();
+ Link<String> tagStack = const Link<String>();
+
+ void pushTag(String tag) {
+ tagStack = tagStack.prepend(tag);
+ indentMore();
+ }
+
+ String popTag() {
+ assert(!tagStack.isEmpty);
+ String tag = tagStack.head;
+ tagStack = tagStack.tail;
+ indentLess();
+ return tag;
+ }
+
+ /**
+ * Adds given string to result string.
+ */
+ void add(String string) {
+ sb.write(string);
+ }
+
+ /// Adds default parameters for [node] into [params].
+ void addDefaultParameters(N node, Map params) {}
+
+ /**
+ * Adds given node type to result string.
+ * The method "opens" the node, meaning that all output after calling
+ * this method and before calling closeNode() will represent contents
+ * of given node.
+ */
+ void openNode(N node, String type, [Map params]) {
+ if (params == null) params = new Map();
+ addCurrentIndent();
+ sb.write("<");
+ addDefaultParameters(node, params);
+ addTypeWithParams(type, params);
+ sb.write(">\n");
+ pushTag(type);
+ }
+
+ /**
+ * Adds given node to result string.
+ */
+ void openAndCloseNode(N node, String type, [Map params]) {
+ if (params == null) params = {};
+ addCurrentIndent();
+ sb.write("<");
+ addDefaultParameters(node, params);
+ addTypeWithParams(type, params);
+ sb.write("/>\n");
+ }
+
+ /**
+ * Closes current node type.
+ */
+ void closeNode() {
+ String tag = popTag();
+ addCurrentIndent();
+ sb.write("</");
+ addTypeWithParams(tag);
+ sb.write(">\n");
+ }
+
+ void addTypeWithParams(String type, [Map params]) {
+ if (params == null) params = new Map();
+ sb.write("${type}");
+ params.forEach((k, v) {
+ String value;
+ if (v != null) {
+ String str = valueToString(v);
+ value = str
+ .replaceAll("<", "<")
+ .replaceAll(">", ">")
+ .replaceAll('"', "'");
+ } else {
+ value = "[null]";
+ }
+ sb.write(' $k="$value"');
+ });
+ }
+
+ void addCurrentIndent() {
+ sb.write(indentation);
+ }
+
+ /// Converts a parameter value into a string.
+ String valueToString(var value) => value;
+}
diff --git a/pkg/compiler/lib/src/util/link.dart b/pkg/compiler/lib/src/util/link.dart
index b45ea6c..31316e9 100644
--- a/pkg/compiler/lib/src/util/link.dart
+++ b/pkg/compiler/lib/src/util/link.dart
@@ -132,7 +132,7 @@
List<T> toList();
- void addLast(T t);
+ Link<T> addLast(T t);
final int length;
final bool isEmpty;
diff --git a/pkg/compiler/lib/src/util/link_implementation.dart b/pkg/compiler/lib/src/util/link_implementation.dart
index 5359102..7cea7d9 100644
--- a/pkg/compiler/lib/src/util/link_implementation.dart
+++ b/pkg/compiler/lib/src/util/link_implementation.dart
@@ -180,7 +180,7 @@
return list;
}
- void addLast(T t) {
+ Link<T> addLast(T t) {
length++;
LinkEntry<T> entry = new LinkEntry<T>(t, null);
if (head == null) {
@@ -189,6 +189,7 @@
lastLink.tail = entry;
}
lastLink = entry;
+ return entry;
}
bool get isEmpty => length == 0;
diff --git a/pkg/compiler/pubspec.yaml b/pkg/compiler/pubspec.yaml
index a078492..d1f45c5 100644
--- a/pkg/compiler/pubspec.yaml
+++ b/pkg/compiler/pubspec.yaml
@@ -3,7 +3,7 @@
name: compiler
#version: do-not-upload
dependencies:
- package_config: ^0.0.4
+ package_config: ^0.1.1
js_ast:
path: ../js_ast
js_runtime:
diff --git a/pkg/dart2js_incremental/lib/library_updater.dart b/pkg/dart2js_incremental/lib/library_updater.dart
index 924f2f4..f111d4a 100644
--- a/pkg/dart2js_incremental/lib/library_updater.dart
+++ b/pkg/dart2js_incremental/lib/library_updater.dart
@@ -70,8 +70,8 @@
Class,
Method;
-import 'package:compiler/src/js_emitter/program_builder.dart' show
- ProgramBuilder;
+import 'package:compiler/src/js_emitter/program_builder/program_builder.dart'
+ show ProgramBuilder;
import 'package:js_runtime/shared/embedded_names.dart'
as embeddedNames;
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index 33b5c58..95c8819 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -588,6 +588,12 @@
implements Declaration, Parameter, Comparable {
accept(NodeVisitor visitor) => visitor.visitName(this);
+ /// Returns a unique [key] for this name.
+ ///
+ /// The key is unrelated to the actual name and is not intended for human
+ /// consumption. As such, it might be long or cryptic.
+ String get key;
+
bool get allowRename => false;
}
@@ -597,6 +603,10 @@
LiteralStringFromName(this.name) : super(null);
String get value => '"${name.name}"';
+
+ void visitChildren(NodeVisitor visitor) {
+ name.accept(visitor);
+ }
}
class LiteralExpression extends Expression {
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index ff757b4..8523916 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.dart
@@ -5,15 +5,21 @@
part of js_ast;
+typedef String Renamer(Name);
+
class JavaScriptPrintingOptions {
final bool shouldCompressOutput;
final bool minifyLocalVariables;
final bool preferSemicolonToNewlineInMinifiedOutput;
+ final Renamer renamerForNames;
JavaScriptPrintingOptions(
{this.shouldCompressOutput: false,
this.minifyLocalVariables: false,
- this.preferSemicolonToNewlineInMinifiedOutput: false});
+ this.preferSemicolonToNewlineInMinifiedOutput: false,
+ this.renamerForNames: identityRenamer});
+
+ static String identityRenamer(Name name) => name.name;
}
@@ -360,7 +366,9 @@
out("else");
if (elsePart is If) {
pendingSpace = true;
+ startNode(elsePart);
ifOut(elsePart, false);
+ endNode(elsePart);
} else {
blockBody(unwrapBlockIfSingleStatement(elsePart),
needsSeparation: true, needsNewline: true);
@@ -628,7 +636,10 @@
VarCollector vars = new VarCollector();
vars.visitFunctionDeclaration(declaration);
indent();
- functionOut(declaration.function, declaration.name, vars);
+ startNode(declaration.function);
+ currentNode.closingPosition =
+ functionOut(declaration.function, declaration.name, vars);
+ endNode(declaration.function);
lineOut();
}
@@ -924,13 +935,17 @@
if (isValidJavaScriptId(fieldWithQuotes)) {
if (access.receiver is LiteralNumber) out(" ", isWhitespace: true);
out(".");
+ startNode(selector);
out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1));
+ endNode(selector);
return;
}
} else if (selector is Name) {
if (access.receiver is LiteralNumber) out(" ", isWhitespace: true);
out(".");
- out(selector.name);
+ startNode(selector);
+ selector.accept(this);
+ endNode(selector);
return;
}
out("[");
@@ -998,7 +1013,7 @@
@override
visitName(Name node) {
- out(node.name);
+ out(options.renamerForNames(node));
}
@override
@@ -1022,7 +1037,9 @@
// in last position. Otherwise `[,]` (having length 1) would become
// equal to `[]` (the empty array)
// and [1,,] (array with 1 and a hole) would become [1,] = [1].
+ startNode(element);
out(",");
+ endNode(element);
continue;
}
if (i != 0) spaceOut();
@@ -1069,6 +1086,7 @@
@override
void visitProperty(Property node) {
+ startNode(node.name);
if (node.name is LiteralString) {
LiteralString nameString = node.name;
String name = nameString.value;
@@ -1084,6 +1102,7 @@
LiteralNumber nameNumber = node.name;
out(nameNumber.value);
}
+ endNode(node.name);
out(":");
spaceOut();
visitNestedExpression(node.value, ASSIGNMENT,
diff --git a/pkg/js_ast/test/printer_callback_test.dart b/pkg/js_ast/test/printer_callback_test.dart
index b4ff074..dbd248b 100644
--- a/pkg/js_ast/test/printer_callback_test.dart
+++ b/pkg/js_ast/test/printer_callback_test.dart
@@ -12,14 +12,26 @@
import 'package:unittest/unittest.dart';
enum TestMode {
+ INPUT,
NONE,
ENTER,
DELIMITER,
EXIT,
}
-const DATA = const [
- const {
+class TestCase {
+ final Map<TestMode, String> data;
+
+ /// Map from template names to the inserted values.
+ final Map<String, String> environment;
+
+ const TestCase(
+ this.data,
+ [this.environment = const {}]);
+}
+
+const List<TestCase> DATA = const <TestCase>[
+ const TestCase(const {
TestMode.NONE: """
function(a, b) {
return null;
@@ -36,15 +48,15 @@
function(a@1, b@2) {
return null@5;
@4}@3@0"""
- },
+ }),
- const {
+ const TestCase(const {
TestMode.NONE: """
function() {
if (true) {
foo1();
foo2();
- } else {
+ } else if (false) {
bar1();
bar2();
}
@@ -58,13 +70,13 @@
@2if (@3true) @4{
@5@6@7foo1();
@8@9@10foo2();
- } else @11{
- @12@13@14bar1();
- @15@16@17bar2();
+ } else @11if (@12false) @13{
+ @14@15@16bar1();
+ @17@18@19bar2();
}
- @18while (@19false) @20{
- @21@22@23baz3();
- @24@25@26baz4();
+ @20while (@21false) @22{
+ @23@24@25baz3();
+ @26@27@28baz4();
}
}""",
TestMode.DELIMITER: """
@@ -72,7 +84,7 @@
if (true) {
foo1();
foo2();
- } else {
+ } else if (false) {
bar1();
bar2();
}
@@ -86,26 +98,107 @@
if (true@3) {
foo1@7()@6;
@5 foo2@10()@9;
-@8 }@4 else {
- bar1@14()@13;
-@12 bar2@17()@16;
-@15 }@11
-@2 while (false@19) {
- baz3@23()@22;
-@21 baz4@26()@25;
-@24 }@20
-@18}@1@0""",
- },
+@8 }@4 else if (false@12) {
+ bar1@16()@15;
+@14 bar2@19()@18;
+@17 }@13
+@11@2 while (false@21) {
+ baz3@25()@24;
+@23 baz4@28()@27;
+@26 }@22
+@20}@1@0""",
+ }),
+
+ const TestCase(const {
+ TestMode.NONE: """
+function() {
+ function foo() {
+ }
+}""",
+ TestMode.ENTER: """
+@0function() @1{
+ @2@3function @4foo() @5{
+ }
+}""",
+ TestMode.DELIMITER: """
+function() {
+ function foo() {
+ @3}
+@0}""",
+ TestMode.EXIT: """
+function() {
+ function foo@4() {
+ }@5@3
+@2}@1@0"""
+ }),
+
+ const TestCase(const {
+ TestMode.INPUT: """
+function() {
+ a['b'];
+ [1,, 2];
+}""",
+ TestMode.NONE: """
+function() {
+ a.b;
+ [1,, 2];
+}""",
+ TestMode.ENTER: """
+@0function() @1{
+ @2@3@4a.@5b;
+ @6@7[@81,@9, @102];
+}""",
+ TestMode.DELIMITER: """
+function() {
+ a.b;
+ [1,, 2];
+@0}""",
+ TestMode.EXIT: """
+function() {
+ a@4.b@5@3;
+@2 [1@8,,@9 2@10]@7;
+@6}@1@0""",
+ }),
+
+ const TestCase(const {
+ TestMode.INPUT: "a.#nameTemplate",
+ TestMode.NONE: "a.nameValue",
+ TestMode.ENTER: "@0@1a.@2nameValue",
+ TestMode.DELIMITER: "a.nameValue",
+ TestMode.EXIT: "a@1.nameValue@2@0",
+ }, const {'nameTemplate': 'nameValue'}),
];
-void check(Map<TestMode, String> map) {
- String code = map[TestMode.NONE];
+class FixedName extends Name {
+ final String name;
+ String get key => name;
+
+ FixedName(this.name);
+
+ @override
+ int compareTo(other) => 0;
+}
+
+void check(TestCase testCase) {
+ Map<TestMode, String> map = testCase.data;
+ String code = map[TestMode.INPUT];
+ if (code == null) {
+ // Input is the same as output.
+ code = map[TestMode.NONE];
+ }
JavaScriptPrintingOptions options = new JavaScriptPrintingOptions();
- Node node = js.parseForeignJS(code).instantiate({});
+ Map arguments = {};
+ testCase.environment.forEach((String name, String value) {
+ arguments[name] = new FixedName(value);
+ });
+ Node node = js.parseForeignJS(code).instantiate(arguments);
map.forEach((TestMode mode, String expectedOutput) {
+ if (mode == TestMode.INPUT) return;
Context context = new Context(mode);
new Printer(options, context).visit(node);
- expect(context.getText(), equals(expectedOutput),
+ // TODO(johnniwinther): Remove `replaceAll(...)` when dart2js behaves as the
+ // VM on newline in multiline strings.
+ expect(context.getText(), equals(expectedOutput.replaceAll('\r\n', '\n')),
reason: "Unexpected output for $code in $mode");
});
}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 5bb70dc..f105670 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -150,47 +150,46 @@
analyzer/test/*: PubGetError
[ $compiler == dart2js && $cps_ir ]
-analyzer/test/cancelable_future_test: Crash # Invalid argument(s)
-analyzer/test/enum_test: Crash # Invalid argument(s)
-analyzer/test/file_system/memory_file_system_test: Crash # Invalid argument(s)
-analyzer/test/file_system/physical_resource_provider_test: Crash # Invalid argument(s)
-analyzer/test/file_system/resource_uri_resolver_test: Crash # Invalid argument(s)
-analyzer/test/generated/all_the_rest_test: Crash # Invalid argument(s)
-analyzer/test/generated/ast_test: Crash # Invalid argument(s)
-analyzer/test/generated/compile_time_error_code_test: Crash # Invalid argument(s)
-analyzer/test/generated/element_test: Crash # Invalid argument(s)
-analyzer/test/generated/incremental_resolver_test: Crash # Invalid argument(s)
-analyzer/test/generated/incremental_scanner_test: Crash # Invalid argument(s)
-analyzer/test/generated/java_core_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-analyzer/test/generated/non_error_resolver_test: Crash # Invalid argument(s)
-analyzer/test/generated/parser_test: Crash # Invalid argument(s)
-analyzer/test/generated/resolver_test: Crash # Invalid argument(s)
-analyzer/test/generated/scanner_test: Crash # Invalid argument(s)
-analyzer/test/generated/static_type_warning_code_test: Crash # Invalid argument(s)
-analyzer/test/generated/static_warning_code_test: Crash # Invalid argument(s)
-analyzer/test/generated/type_system_test: Crash # Invalid argument(s)
-analyzer/test/generated/utilities_test: Crash # Invalid argument(s)
-analyzer/test/instrumentation/instrumentation_test: Crash # Invalid argument(s)
-analyzer/test/parse_compilation_unit_test: Crash # Invalid argument(s)
-analyzer/test/source/package_map_provider_test: Crash # Invalid argument(s)
-analyzer/test/source/package_map_resolver_test: Crash # Invalid argument(s)
-analyzer/test/src/context/cache_test: Crash # Invalid argument(s)
-analyzer/test/src/context/context_test: Crash # Invalid argument(s)
-analyzer/test/src/task/dart_test: Crash # Invalid argument(s)
-analyzer/test/src/task/dart_work_manager_test: Crash # Invalid argument(s)
-analyzer/test/src/task/driver_test: Crash # Invalid argument(s)
-analyzer/test/src/task/general_test: Crash # Invalid argument(s)
-analyzer/test/src/task/html_test: Crash # Invalid argument(s)
-analyzer/test/src/task/html_work_manager_test: Crash # Invalid argument(s)
-analyzer/test/src/task/incremental_element_builder_test: Crash # Invalid argument(s)
-analyzer/test/src/task/inputs_test: Crash # Invalid argument(s)
-analyzer/test/src/task/manager_test: Crash # Invalid argument(s)
-analyzer/test/src/task/model_test: Crash # Invalid argument(s)
-analyzer/test/src/util/asserts_test: Crash # Invalid argument(s)
-analyzer/test/src/util/lru_map_test: Crash # Invalid argument(s)
-fixnum/test/int_32_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-fixnum/test/int_64_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-typed_data/test/typed_buffers_test/01: Crash # Invalid argument(s)
-typed_data/test/typed_buffers_test/none : RuntimeError # TypeError: receiver.get$_nums is not a function
-typed_mock/test/typed_mock_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-analyzer/test/generated/source_factory_test : Crash # Invalid argument(s)
+analyzer/test/cancelable_future_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/enum_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/file_system/memory_file_system_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/file_system/physical_resource_provider_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/file_system/resource_uri_resolver_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/all_the_rest_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/ast_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/compile_time_error_code_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/element_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/incremental_resolver_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/incremental_scanner_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/java_core_test: RuntimeError # receiver.get$_nums is not a function
+analyzer/test/generated/non_error_resolver_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/parser_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/resolver_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/scanner_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/source_factory_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/static_type_warning_code_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/static_warning_code_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/type_system_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/generated/utilities_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/instrumentation/instrumentation_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/parse_compilation_unit_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/source/package_map_provider_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/source/package_map_resolver_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/context/cache_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/context/context_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/dart_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/dart_work_manager_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/driver_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/general_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/html_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/html_work_manager_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/incremental_element_builder_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/inputs_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/manager_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/task/model_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/util/asserts_test: Crash # The null object does not have a getter '_element'.
+analyzer/test/src/util/lru_map_test: Crash # The null object does not have a getter '_element'.
+fixnum/test/int_32_test: RuntimeError # receiver.get$_nums is not a function
+fixnum/test/int_64_test: RuntimeError # receiver.get$_nums is not a function
+typed_data/test/typed_buffers_test/none: RuntimeError # receiver.get$_nums is not a function
+typed_mock/test/typed_mock_test: RuntimeError # receiver.get$_nums is not a function
diff --git a/pkg/pkgbuild.status b/pkg/pkgbuild.status
index a2c33a4..3cf95f1 100644
--- a/pkg/pkgbuild.status
+++ b/pkg/pkgbuild.status
@@ -8,20 +8,17 @@
[ $use_repository_packages ]
pkg/analyzer: PubGetError
-pkg/compiler: PubGetError # Issue 23750
samples/third_party/angular_todo: Fail # angular needs to be updated
samples/third_party/todomvc_performance: Skip # dependencies are not in the repo
-third_party/pkg/package_config: PubGetError # Issue 23750
[ $use_public_packages ]
-pkg/compiler: PubGetError # Issue 23750
+pkg/compiler: SkipByDesign # js_ast is not published
samples/third_party/angular_todo: Pass, Slow
-third_party/pkg/async_await: Skip # Uses expect package.
samples/third_party/todomvc_performance: Pass, Slow
[ $builder_tag == russian ]
samples/third_party/angular_todo: Fail # Issue 16356
-samples/third_party/dromaeo: Fail # Issue 23750
+samples/third_party/dromaeo: Fail # Issue 23760
[ $use_public_packages && $system == windows ]
samples/third_party/todomvc_performance: Fail # Issue 18086
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index 2ee4114..bbc4d3e 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -143,8 +143,11 @@
executable("gen_snapshot") {
configs += ["..:dart_config"]
deps = [
- ":libdart_nosnapshot",
+ ":generate_builtin_cc_file",
+ ":generate_io_cc_file",
+ ":generate_io_patch_cc_file",
":libdart_builtin",
+ ":libdart_nosnapshot",
]
sources = [
diff --git a/runtime/lib/bigint.dart b/runtime/lib/bigint.dart
index ce151cf..a278595 100644
--- a/runtime/lib/bigint.dart
+++ b/runtime/lib/bigint.dart
@@ -1222,7 +1222,7 @@
// This method must support smi._toBigint()._shrFromInt(int).
int _shrFromInt(int other) {
if (_used == 0) return other; // Shift amount is zero.
- if (_neg) throw new RangeError(this);
+ if (_neg) throw new RangeError.range(this, 0, null);
assert(_DIGIT_BITS == 32); // Otherwise this code needs to be revised.
var shift;
if ((_used > 2) || ((_used == 2) && (_digits[1] > 0x10000000))) {
@@ -1241,7 +1241,7 @@
// An out of memory exception is thrown if the result cannot be allocated.
int _shlFromInt(int other) {
if (_used == 0) return other; // Shift amount is zero.
- if (_neg) throw new RangeError(this);
+ if (_neg) throw new RangeError.range(this, 0, null);
assert(_DIGIT_BITS == 32); // Otherwise this code needs to be revised.
var shift;
if (_used > 2 || (_used == 2 && _digits[1] > 0x10000000)) {
@@ -1399,10 +1399,14 @@
// Returns pow(this, e) % m, with e >= 0, m > 0.
int modPow(int e, int m) {
- if (e is! int) throw new ArgumentError(e);
- if (m is! int) throw new ArgumentError(m);
- if (e < 0) throw new RangeError(e);
- if (m <= 0) throw new RangeError(m);
+ if (e is! int) {
+ throw new ArgumentError.value(e, "exponent", "not an integer");
+ }
+ if (m is! int) {
+ throw new ArgumentError.value(m, "modulus", "not an integer");
+ }
+ if (e < 0) throw new RangeError.range(e, 0, null, "exponent");
+ if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
if (e == 0) return 1;
m = m._toBigint();
final m_used = m._used;
@@ -1543,7 +1547,7 @@
// If inv is false, returns gcd(x, y).
// If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1.
- // If inv is true and gcd(x, y) != 1, throws RangeError("Not coprime").
+ // If inv is true and gcd(x, y) != 1, throws Exception("Not coprime").
static int _binaryGcd(_Bigint x, _Bigint y, bool inv) {
var x_digits = x._digits;
var y_digits = y._digits;
@@ -1557,10 +1561,15 @@
if (inv) {
if ((y_used == 1) && (y_digits[0] == 1)) return 1;
if ((y_used == 0) || (y_digits[0].isEven && x_digits[0].isEven)) {
- throw new RangeError("Not coprime");
+ throw new Exception("Not coprime");
}
} else {
- if ((x_used == 0) || (y_used == 0)) throw new RangeError(0);
+ if (x_used == 0) {
+ throw new ArgumentError.value(0, "this", "must not be zero");
+ }
+ if (y_used == 0) {
+ throw new ArgumentError.value(0, "other", "must not be zero");
+ }
if (((x_used == 1) && (x_digits[0] == 1)) ||
((y_used == 1) && (y_digits[0] == 1))) return 1;
bool xy_cloned = false;
@@ -1756,7 +1765,9 @@
// No inverse if v != 1.
var i = m_used - 1;
while ((i > 0) && (v_digits[i] == 0)) --i;
- if ((i != 0) || (v_digits[0] != 1)) throw new RangeError("Not coprime");
+ if ((i != 0) || (v_digits[0] != 1)) {
+ throw new Exception("Not coprime");
+ }
if (d_neg) {
if ((d_digits[m_used] != 0) ||
@@ -1786,8 +1797,10 @@
// Returns 1/this % m, with m > 0.
int modInverse(int m) {
- if (m is! int) throw new ArgumentError(m);
- if (m <= 0) throw new RangeError(m);
+ if (m is! int) {
+ throw new ArgumentError.value(m, "modulus", "not an integer");
+ }
+ if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
if (m == 1) return 0;
m = m._toBigint();
var t = this;
@@ -1798,9 +1811,14 @@
return _binaryGcd(m, t, true);
}
- // Returns gcd of abs(this) and abs(other), with this != 0 and other !=0.
+ // Returns gcd of abs(this) and abs(other).
int gcd(int other) {
- if (other is! int) throw new ArgumentError(other);
+ if (other is! int) {
+ throw new ArgumentError.value(other, "other", "not an integer");
+ }
+ if (other == 0) {
+ return this.abs();
+ }
return _binaryGcd(this, other._toBigint(), false);
}
}
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index be0cb8f..63f868b 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -175,21 +175,23 @@
double truncateToDouble() { return this.toDouble(); }
num clamp(num lowerLimit, num upperLimit) {
- if (lowerLimit is! num) throw new ArgumentError(lowerLimit);
- if (upperLimit is! num) throw new ArgumentError(upperLimit);
+ if (lowerLimit is! num) {
+ throw new ArgumentError.value(lowerLimit, "lowerLimit");
+ }
+ if (upperLimit is! num) {
+ throw new ArgumentError.value(upperLimit, "upperLimit");
+ }
// Special case for integers.
- if (lowerLimit is int && upperLimit is int) {
- if (lowerLimit > upperLimit) {
- throw new ArgumentError(lowerLimit);
- }
+ if (lowerLimit is int && upperLimit is int &&
+ lowerLimit <= upperLimit) {
if (this < lowerLimit) return lowerLimit;
if (this > upperLimit) return upperLimit;
return this;
}
- // Generic case involving doubles.
+ // Generic case involving doubles, and invalid integer ranges.
if (lowerLimit.compareTo(upperLimit) > 0) {
- throw new ArgumentError(lowerLimit);
+ throw new RangeError.range(upperLimit, lowerLimit, null, "upperLimit");
}
if (lowerLimit.isNaN) return lowerLimit;
// Note that we don't need to care for -0.0 for the lower limit.
@@ -216,8 +218,8 @@
static const _digits = "0123456789abcdefghijklmnopqrstuvwxyz";
String toRadixString(int radix) {
- if (radix is! int || radix < 2 || radix > 36) {
- throw new ArgumentError(radix);
+ if (radix < 2 || 36 < radix) {
+ throw new RangeError.range(radix, 2, 36, "radix");
}
if (radix & (radix - 1) == 0) {
return _toPow2String(radix);
@@ -267,10 +269,14 @@
// Returns pow(this, e) % m.
int modPow(int e, int m) {
- if (e is! int) throw new ArgumentError(e);
- if (m is! int) throw new ArgumentError(m);
- if (e < 0) throw new RangeError(e);
- if (m <= 0) throw new RangeError(m);
+ if (e is! int) {
+ throw new ArgumentError.value(e, "exponent", "not an integer");
+ }
+ if (m is! int) {
+ throw new ArgumentError.value(m, "modulus", "not an integer");
+ }
+ if (e < 0) throw new RangeError.range(e, 0, null, "exponent");
+ if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
if (e == 0) return 1;
if (e is _Bigint || m is _Bigint) {
return _toBigint().modPow(e, m);
@@ -292,7 +298,7 @@
// If inv is false, returns gcd(x, y).
// If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1.
- // If inv is true and gcd(x, y) != 1, throws RangeError("Not coprime").
+ // If inv is true and gcd(x, y) != 1, throws Exception("Not coprime").
static int _binaryGcd(int x, int y, bool inv) {
int s = 0;
if (!inv) {
@@ -352,7 +358,9 @@
}
} while (u != 0);
if (!inv) return v << s;
- if (v != 1) throw new RangeError("Not coprime");
+ if (v != 1) {
+ throw new Exception("Not coprime");
+ }
if (d < 0) {
d += x;
if (d < 0) d += x;
@@ -365,8 +373,10 @@
// Returns 1/this % m, with m > 0.
int modInverse(int m) {
- if (m is! int) throw new ArgumentError(m);
- if (m <= 0) throw new RangeError(m);
+ if (m is! int) {
+ throw new ArgumentError.value(m, "modulus", "not an integer");
+ }
+ if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
if (m == 1) return 0;
if (m is _Bigint) {
return _toBigint().modInverse(m);
@@ -374,16 +384,21 @@
int t = this;
if ((t < 0) || (t >= m)) t %= m;
if (t == 1) return 1;
- if ((t == 0) || (t.isEven && m.isEven)) throw new RangeError("Not coprime");
+ if ((t == 0) || (t.isEven && m.isEven)) {
+ throw new Exception("Not coprime");
+ }
return _binaryGcd(m, t, true);
}
- // Returns gcd of abs(this) and abs(other), with this != 0 and other !=0.
+ // Returns gcd of abs(this) and abs(other).
int gcd(int other) {
- if (other is! int) throw new ArgumentError(other);
- if ((this == 0) || (other == 0)) throw new RangeError(0);
+ if (other is! int) {
+ throw new ArgumentError.value(other, "other", "not an integer");
+ }
int x = this.abs();
int y = other.abs();
+ if (x == 0) return y;
+ if (y == 0) return x;
if ((x == 1) || (y == 1)) return 1;
if (other is _Bigint) {
return _toBigint().gcd(other);
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index 3ffbff8..33af271 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -18,6 +18,8 @@
namespace dart {
+DECLARE_FLAG(bool, lazy_dispatchers);
+
#define PROPAGATE_IF_MALFORMED(type) \
if (type.IsMalformed()) { \
Exceptions::PropagateError(Error::Handle(type.error())); \
@@ -739,37 +741,6 @@
}
-static RawInstance* InvokeInstanceGetter(const Class& klass,
- const Instance& reflectee,
- const String& getter_name,
- const bool throw_nsm_if_absent) {
- const String& internal_getter_name = String::Handle(
- Field::GetterName(getter_name));
- Function& function = Function::Handle(
- Resolver::ResolveDynamicAnyArgs(klass, internal_getter_name));
-
- if (!function.IsNull() || throw_nsm_if_absent) {
- const int kNumArgs = 1;
- const Array& args = Array::Handle(Array::New(kNumArgs));
- args.SetAt(0, reflectee);
- const Array& args_descriptor =
- Array::Handle(ArgumentsDescriptor::New(args.Length()));
-
- // InvokeDynamic invokes NoSuchMethod if the provided function is null.
- return InvokeDynamicFunction(reflectee,
- function,
- internal_getter_name,
- args,
- args_descriptor);
- }
-
- // Fall through case: Indicate that we didn't find any function or field using
- // a special null instance. This is different from a field being null. Callers
- // make sure that this null does not leak into Dartland.
- return Object::sentinel().raw();
-}
-
-
static RawAbstractType* InstantiateType(const AbstractType& type,
const AbstractType& instantiator) {
ASSERT(type.IsFinalized());
@@ -1389,7 +1360,34 @@
GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2));
Class& klass = Class::Handle(reflectee.clazz());
- return InvokeInstanceGetter(klass, reflectee, getter_name, true);
+
+ const String& internal_getter_name = String::Handle(
+ Field::GetterName(getter_name));
+ Function& function = Function::Handle(
+ Resolver::ResolveDynamicAnyArgs(klass, internal_getter_name));
+
+ // Check for method extraction when method extractors are not created.
+ if (function.IsNull() && !FLAG_lazy_dispatchers) {
+ function = Resolver::ResolveDynamicAnyArgs(klass, getter_name);
+ if (!function.IsNull()) {
+ const Function& closure_function =
+ Function::Handle(function.ImplicitClosureFunction());
+ return closure_function.ImplicitInstanceClosure(reflectee);
+ }
+ }
+
+ const int kNumArgs = 1;
+ const Array& args = Array::Handle(Array::New(kNumArgs));
+ args.SetAt(0, reflectee);
+ const Array& args_descriptor =
+ Array::Handle(ArgumentsDescriptor::New(args.Length()));
+
+ // InvokeDynamic invokes NoSuchMethod if the provided function is null.
+ return InvokeDynamicFunction(reflectee,
+ function,
+ internal_getter_name,
+ args,
+ args_descriptor);
}
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index fe88b85..2c43d96 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -207,6 +207,66 @@
}
+DEFINE_NATIVE_ENTRY(Object_instanceOfNum, 2) {
+ const Instance& instance =
+ Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
+ const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
+ bool is_instance_of = instance.IsNumber();
+ if (negate.value()) {
+ is_instance_of = !is_instance_of;
+ }
+ return Bool::Get(is_instance_of).raw();
+}
+
+
+DEFINE_NATIVE_ENTRY(Object_instanceOfInt, 2) {
+ const Instance& instance =
+ Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
+ const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
+ bool is_instance_of = instance.IsInteger();
+ if (negate.value()) {
+ is_instance_of = !is_instance_of;
+ }
+ return Bool::Get(is_instance_of).raw();
+}
+
+
+DEFINE_NATIVE_ENTRY(Object_instanceOfSmi, 2) {
+ const Instance& instance =
+ Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
+ const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
+ bool is_instance_of = instance.IsSmi();
+ if (negate.value()) {
+ is_instance_of = !is_instance_of;
+ }
+ return Bool::Get(is_instance_of).raw();
+}
+
+
+DEFINE_NATIVE_ENTRY(Object_instanceOfDouble, 2) {
+ const Instance& instance =
+ Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
+ const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
+ bool is_instance_of = instance.IsDouble();
+ if (negate.value()) {
+ is_instance_of = !is_instance_of;
+ }
+ return Bool::Get(is_instance_of).raw();
+}
+
+
+DEFINE_NATIVE_ENTRY(Object_instanceOfString, 2) {
+ const Instance& instance =
+ Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
+ const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
+ bool is_instance_of = instance.IsString();
+ if (negate.value()) {
+ is_instance_of = !is_instance_of;
+ }
+ return Bool::Get(is_instance_of).raw();
+}
+
+
DEFINE_NATIVE_ENTRY(Object_as, 4) {
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
// Instantiator at position 1 is not used. It is passed along so that the call
diff --git a/runtime/lib/object_patch.dart b/runtime/lib/object_patch.dart
index 52d7546..be42a57 100644
--- a/runtime/lib/object_patch.dart
+++ b/runtime/lib/object_patch.dart
@@ -59,6 +59,12 @@
bool negate)
native "Object_instanceOf";
+ bool _instanceOfDouble(bool negate) native "Object_instanceOfDouble";
+ bool _instanceOfNum(bool negate) native "Object_instanceOfNum";
+ bool _instanceOfInt(bool negate) native "Object_instanceOfInt";
+ bool _instanceOfSmi(bool negate) native "Object_instanceOfSmi";
+ bool _instanceOfString(bool negate) native "Object_instanceOfString";
+
// Call this function instead of inlining 'as', thus collecting type
// feedback. Returns receiver.
_as(instantiator, instantiator_type_arguments, type) native "Object_as";
diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc
index 6ffeb6d..6fd9d6f 100644
--- a/runtime/lib/regexp.cc
+++ b/runtime/lib/regexp.cc
@@ -8,11 +8,14 @@
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/regexp_parser.h"
+#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp_assembler_bytecode.h"
#include "vm/thread.h"
namespace dart {
DECLARE_FLAG(bool, trace_irregexp);
+DECLARE_FLAG(bool, interpret_irregexp);
DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_factory, 4) {
@@ -82,18 +85,23 @@
DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_ExecuteMatch, 3) {
const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->NativeArgAt(0));
ASSERT(!regexp.IsNull());
- GET_NON_NULL_NATIVE_ARGUMENT(String, str, arguments->NativeArgAt(1));
+ GET_NON_NULL_NATIVE_ARGUMENT(String, subject, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, start_index, arguments->NativeArgAt(2));
+ if (FLAG_interpret_irregexp) {
+ return BytecodeRegExpMacroAssembler::Interpret(regexp, subject, start_index,
+ zone);
+ }
+
// This function is intrinsified. See Intrinsifier::JSRegExp_ExecuteMatch.
- const intptr_t cid = str.GetClassId();
+ const intptr_t cid = subject.GetClassId();
// Retrieve the cached function.
const Function& fn = Function::Handle(regexp.function(cid));
ASSERT(!fn.IsNull());
// And finally call the generated code.
- return IRRegExpMacroAssembler::Execute(fn, str, start_index, zone);
+ return IRRegExpMacroAssembler::Execute(fn, subject, start_index, zone);
}
} // namespace dart
diff --git a/runtime/observatory/.gitignore b/runtime/observatory/.gitignore
index c73eaff..06adbfb 100644
--- a/runtime/observatory/.gitignore
+++ b/runtime/observatory/.gitignore
@@ -3,3 +3,4 @@
build
.pub
.idea
+.packages
\ No newline at end of file
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 6367d4d..28d2c56 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -1563,7 +1563,10 @@
}
-void Assembler::LoadObject(Register rd, const Object& object, Condition cond) {
+void Assembler::LoadObjectHelper(Register rd,
+ const Object& object,
+ Condition cond,
+ bool is_unique) {
// Smis and VM heap objects are never relocated; do not use object pool.
if (object.IsSmi()) {
LoadImmediate(rd, reinterpret_cast<int32_t>(object.raw()), cond);
@@ -1574,13 +1577,26 @@
} else {
// Make sure that class CallPattern is able to decode this load from the
// object pool.
- const int32_t offset =
- ObjectPool::element_offset(object_pool_wrapper_.FindObject(object));
+ const int32_t offset = ObjectPool::element_offset(
+ is_unique ? object_pool_wrapper_.AddObject(object)
+ : object_pool_wrapper_.FindObject(object));
LoadWordFromPoolOffset(rd, offset - kHeapObjectTag, cond);
}
}
+void Assembler::LoadObject(Register rd, const Object& object, Condition cond) {
+ LoadObjectHelper(rd, object, cond, false);
+}
+
+
+void Assembler::LoadUniqueObject(Register rd,
+ const Object& object,
+ Condition cond) {
+ LoadObjectHelper(rd, object, cond, true);
+}
+
+
void Assembler::LoadExternalLabel(Register rd,
const ExternalLabel* label,
Patchability patchable,
@@ -1966,13 +1982,18 @@
}
-void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
static const intptr_t kSmiCidSource = kSmiCid << RawObject::kClassIdTagPos;
LoadImmediate(TMP, reinterpret_cast<int32_t>(&kSmiCidSource) + 1);
tst(object, Operand(kSmiTagMask));
mov(TMP, Operand(object), NE);
LoadClassId(result, TMP);
+}
+
+
+void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+ LoadClassIdMayBeSmi(result, object);
SmiTag(result);
}
diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h
index 24e41b0..f3d9583 100644
--- a/runtime/vm/assembler_arm.h
+++ b/runtime/vm/assembler_arm.h
@@ -666,6 +666,7 @@
void LoadIsolate(Register rd);
void LoadObject(Register rd, const Object& object, Condition cond = AL);
+ void LoadUniqueObject(Register rd, const Object& object, Condition cond = AL);
void LoadExternalLabel(Register dst,
const ExternalLabel* label,
Patchability patchable,
@@ -739,6 +740,7 @@
void LoadClassById(Register result, Register class_id);
void LoadClass(Register result, Register object, Register scratch);
void CompareClassId(Register object, intptr_t class_id, Register scratch);
+ void LoadClassIdMayBeSmi(Register result, Register object);
void LoadTaggedClassIdMayBeSmi(Register result, Register object);
void ComputeRange(Register result,
@@ -1000,6 +1002,11 @@
bool allow_constant_pool_;
+ void LoadObjectHelper(Register rd,
+ const Object& object,
+ Condition cond,
+ bool is_unique);
+
void EmitType01(Condition cond,
int type,
Opcode opcode,
diff --git a/runtime/vm/assembler_arm64.cc b/runtime/vm/assembler_arm64.cc
index 7b71e57..4e39862 100644
--- a/runtime/vm/assembler_arm64.cc
+++ b/runtime/vm/assembler_arm64.cc
@@ -52,13 +52,6 @@
} else {
object_pool_wrapper_.AddObject(vacant);
}
-
- if (stub_code->CallToRuntime_entry() != NULL) {
- object_pool_wrapper_.AddExternalLabel(
- &stub_code->CallToRuntimeLabel(), kNotPatchable);
- } else {
- object_pool_wrapper_.AddObject(vacant);
- }
}
}
@@ -464,10 +457,14 @@
}
-void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
+void Assembler::LoadObjectHelper(Register dst,
+ const Object& object,
+ Register pp,
+ bool is_unique) {
if (CanLoadObjectFromPool(object)) {
- const int32_t offset =
- ObjectPool::element_offset(object_pool_wrapper_.FindObject(object));
+ const int32_t offset = ObjectPool::element_offset(
+ is_unique ? object_pool_wrapper_.AddObject(object)
+ : object_pool_wrapper_.FindObject(object));
LoadWordFromPoolOffset(dst, pp, offset);
} else {
ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
@@ -478,6 +475,18 @@
}
+void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
+ LoadObjectHelper(dst, object, pp, false);
+}
+
+
+void Assembler::LoadUniqueObject(Register dst,
+ const Object& object,
+ Register pp) {
+ LoadObjectHelper(dst, object, pp, true);
+}
+
+
void Assembler::CompareObject(Register reg, const Object& object, Register pp) {
if (CanLoadObjectFromPool(object)) {
LoadObject(TMP, object, pp);
@@ -1016,7 +1025,7 @@
}
-void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
// Load up a null object. We only need it so we can use LoadClassId on it in
// the case that object is a Smi..
LoadObject(TMP, Object::null_object(), PP);
@@ -1031,6 +1040,11 @@
LoadImmediate(TMP, kSmiCid, PP);
// If object is a Smi, move the Smi cid into result. o/w leave alone.
csel(result, TMP, result, EQ);
+}
+
+
+void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+ LoadClassIdMayBeSmi(result, object);
// Finally, tag the result.
SmiTag(result);
}
diff --git a/runtime/vm/assembler_arm64.h b/runtime/vm/assembler_arm64.h
index 0ac4a69..1da9605 100644
--- a/runtime/vm/assembler_arm64.h
+++ b/runtime/vm/assembler_arm64.h
@@ -1321,6 +1321,7 @@
Register pp);
void LoadIsolate(Register dst, Register pp);
void LoadObject(Register dst, const Object& obj, Register pp);
+ void LoadUniqueObject(Register dst, const Object& obj, Register pp);
void LoadDecodableImmediate(Register reg, int64_t imm, Register pp);
void LoadImmediateFixed(Register reg, int64_t imm);
void LoadImmediate(Register reg, int64_t imm, Register pp);
@@ -1336,6 +1337,7 @@
void LoadClassById(Register result, Register class_id, Register pp);
void LoadClass(Register result, Register object, Register pp);
void CompareClassId(Register object, intptr_t class_id, Register pp);
+ void LoadClassIdMayBeSmi(Register result, Register object);
void LoadTaggedClassIdMayBeSmi(Register result, Register object);
void ComputeRange(Register result,
@@ -1443,6 +1445,11 @@
bool allow_constant_pool_;
+ void LoadObjectHelper(Register dst,
+ const Object& obj,
+ Register pp,
+ bool is_unique);
+
void AddSubHelper(OperandSize os, bool set_flags, bool subtract,
Register rd, Register rn, Operand o) {
ASSERT((rd != R31) && (rn != R31));
diff --git a/runtime/vm/assembler_ia32.cc b/runtime/vm/assembler_ia32.cc
index bb54355..249fba0 100644
--- a/runtime/vm/assembler_ia32.cc
+++ b/runtime/vm/assembler_ia32.cc
@@ -3017,7 +3017,7 @@
}
-void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
ASSERT(result != object);
static const intptr_t kSmiCidSource = kSmiCid << RawObject::kClassIdTagPos;
@@ -3031,7 +3031,11 @@
// Otherwise, the dummy object is used, and the result is kSmiCid.
cmovne(result, object);
LoadClassId(result, result);
+}
+
+void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+ LoadClassIdMayBeSmi(result, object);
// Tag the result.
SmiTag(result);
}
diff --git a/runtime/vm/assembler_ia32.h b/runtime/vm/assembler_ia32.h
index 1b386a3..7ebea1d 100644
--- a/runtime/vm/assembler_ia32.h
+++ b/runtime/vm/assembler_ia32.h
@@ -742,8 +742,8 @@
void CompareClassId(Register object, intptr_t class_id, Register scratch);
- void LoadTaggedClassIdMayBeSmi(Register result,
- Register object);
+ void LoadClassIdMayBeSmi(Register result, Register object);
+ void LoadTaggedClassIdMayBeSmi(Register result, Register object);
void SmiUntagOrCheckClass(Register object,
intptr_t class_id,
diff --git a/runtime/vm/assembler_mips.cc b/runtime/vm/assembler_mips.cc
index 29644ab..b418fc8 100644
--- a/runtime/vm/assembler_mips.cc
+++ b/runtime/vm/assembler_mips.cc
@@ -456,7 +456,9 @@
}
-void Assembler::LoadObject(Register rd, const Object& object) {
+void Assembler::LoadObjectHelper(Register rd,
+ const Object& object,
+ bool is_unique) {
ASSERT(!in_delay_slot_);
// Smis and VM heap objects are never relocated; do not use object pool.
if (object.IsSmi()) {
@@ -471,13 +473,24 @@
} else {
// Make sure that class CallPattern is able to decode this load from the
// object pool.
- const int32_t offset =
- ObjectPool::element_offset(object_pool_wrapper_.FindObject(object));
+ const int32_t offset = ObjectPool::element_offset(
+ is_unique ? object_pool_wrapper_.AddObject(object)
+ : object_pool_wrapper_.FindObject(object));
LoadWordFromPoolOffset(rd, offset - kHeapObjectTag);
}
}
+void Assembler::LoadObject(Register rd, const Object& object) {
+ LoadObjectHelper(rd, object, false);
+}
+
+
+void Assembler::LoadUniqueObject(Register rd, const Object& object) {
+ LoadObjectHelper(rd, object, true);
+}
+
+
void Assembler::LoadExternalLabel(Register rd,
const ExternalLabel* label,
Patchability patchable) {
@@ -667,7 +680,7 @@
}
-void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
static const intptr_t kSmiCidSource = kSmiCid << RawObject::kClassIdTagPos;
LoadImmediate(TMP, reinterpret_cast<int32_t>(&kSmiCidSource) + 1);
@@ -677,6 +690,11 @@
}
movz(result, TMP, CMPRES1);
LoadClassId(result, result);
+}
+
+
+void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+ LoadClassIdMayBeSmi(result, object);
SmiTag(result);
}
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 79e6539..a4c379b 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -1539,6 +1539,7 @@
void LoadWordFromPoolOffset(Register rd, int32_t offset);
void LoadObject(Register rd, const Object& object);
+ void LoadUniqueObject(Register rd, const Object& object);
void LoadExternalLabel(Register rd,
const ExternalLabel* label,
Patchability patchable);
@@ -1549,6 +1550,7 @@
void LoadClassId(Register result, Register object);
void LoadClassById(Register result, Register class_id);
void LoadClass(Register result, Register object);
+ void LoadClassIdMayBeSmi(Register result, Register object);
void LoadTaggedClassIdMayBeSmi(Register result, Register object);
void ComputeRange(Register result,
@@ -1650,6 +1652,8 @@
bool allow_constant_pool_;
+ void LoadObjectHelper(Register rd, const Object& object, bool is_unique);
+
void Emit(int32_t value) {
// Emitting an instruction clears the delay slot state.
in_delay_slot_ = false;
diff --git a/runtime/vm/assembler_x64.cc b/runtime/vm/assembler_x64.cc
index 02f85df..66b36d0 100644
--- a/runtime/vm/assembler_x64.cc
+++ b/runtime/vm/assembler_x64.cc
@@ -49,13 +49,6 @@
} else {
object_pool_wrapper_.AddObject(vacant);
}
-
- if (stub_code->CallToRuntime_entry() != NULL) {
- object_pool_wrapper_.AddExternalLabel(
- &stub_code->CallToRuntimeLabel(), kNotPatchable);
- } else {
- object_pool_wrapper_.AddObject(vacant);
- }
}
}
@@ -2841,10 +2834,14 @@
}
-void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
+void Assembler::LoadObjectHelper(Register dst,
+ const Object& object,
+ Register pp,
+ bool is_unique) {
if (CanLoadFromObjectPool(object)) {
- const int32_t offset =
- ObjectPool::element_offset(object_pool_wrapper_.FindObject(object));
+ const int32_t offset = ObjectPool::element_offset(
+ is_unique ? object_pool_wrapper_.AddObject(object)
+ : object_pool_wrapper_.FindObject(object));
LoadWordFromPoolOffset(dst, pp, offset - kHeapObjectTag);
} else {
ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
@@ -2855,6 +2852,18 @@
}
+void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
+ LoadObjectHelper(dst, object, pp, false);
+}
+
+
+void Assembler::LoadUniqueObject(Register dst,
+ const Object& object,
+ Register pp) {
+ LoadObjectHelper(dst, object, pp, true);
+}
+
+
void Assembler::StoreObject(const Address& dst, const Object& object,
Register pp) {
if (CanLoadFromObjectPool(object)) {
@@ -3793,7 +3802,7 @@
}
-void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
ASSERT(result != object);
// Load up a null object. We only need it so we can use LoadClassId on it in
@@ -3810,6 +3819,11 @@
movq(object, Immediate(kSmiCid));
// If object is a Smi, move the Smi cid into result. o/w leave alone.
cmoveq(result, object);
+}
+
+
+void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
+ LoadClassIdMayBeSmi(result, object);
// Finally, tag the result.
SmiTag(result);
}
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index c3ea20d..d964931 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -762,6 +762,7 @@
void LoadImmediate(Register reg, const Immediate& imm, Register pp);
void LoadIsolate(Register dst);
void LoadObject(Register dst, const Object& obj, Register pp);
+ void LoadUniqueObject(Register dst, const Object& obj, Register pp);
void LoadExternalLabel(Register dst,
const ExternalLabel* label,
Patchability patchable,
@@ -859,6 +860,7 @@
void CompareClassId(Register object, intptr_t class_id);
+ void LoadClassIdMayBeSmi(Register result, Register object);
void LoadTaggedClassIdMayBeSmi(Register result, Register object);
// CheckClassIs fused with optimistic SmiUntag.
@@ -1066,6 +1068,10 @@
intptr_t FindImmediate(int64_t imm);
bool CanLoadFromObjectPool(const Object& object);
+ void LoadObjectHelper(Register dst,
+ const Object& obj,
+ Register pp,
+ bool is_unique);
void LoadWordFromPoolOffset(Register dst, Register pp, int32_t offset);
inline void EmitUint8(uint8_t value);
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index 227a32b..6784ece 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -21,6 +21,11 @@
V(Object_noSuchMethod, 6) \
V(Object_runtimeType, 1) \
V(Object_instanceOf, 5) \
+ V(Object_instanceOfNum, 2) \
+ V(Object_instanceOfInt, 2) \
+ V(Object_instanceOfSmi, 2) \
+ V(Object_instanceOfDouble, 2) \
+ V(Object_instanceOfString, 2) \
V(Object_as, 4) \
V(Function_apply, 2) \
V(FunctionImpl_equals, 2) \
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 46f3f4d..4af6636 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -248,6 +248,18 @@
#endif // defined(DART_NO_SNAPSHOT).
+static bool IsLoaded(const Type& type) {
+ if (type.HasResolvedTypeClass()) {
+ return true;
+ }
+ const UnresolvedClass& unresolved_class =
+ UnresolvedClass::Handle(type.unresolved_class());
+ const LibraryPrefix& prefix =
+ LibraryPrefix::Handle(unresolved_class.library_prefix());
+ return prefix.IsNull() || prefix.is_loaded();
+}
+
+
// Resolve unresolved_class in the library of cls, or return null.
RawClass* ClassFinalizer::ResolveClass(
const Class& cls,
@@ -275,7 +287,7 @@
const Function& target = Function::Handle(factory.RedirectionTarget());
if (target.IsNull()) {
Type& type = Type::Handle(factory.RedirectionType());
- if (!type.IsMalformed()) {
+ if (!type.IsMalformed() && IsLoaded(type)) {
const GrowableObjectArray& visited_factories =
GrowableObjectArray::Handle(GrowableObjectArray::New());
ResolveRedirectingFactoryTarget(cls, factory, visited_factories);
@@ -1498,9 +1510,13 @@
// The function may be a still unresolved redirecting factory. Do not
// yet try to resolve it in order to avoid cycles in class finalization.
// However, the redirection type should be finalized.
+ // If the redirection type is from a deferred library and is not
+ // yet loaded, do not attempt to resolve.
Type& type = Type::Handle(I, function.RedirectionType());
- type ^= FinalizeType(cls, type, kCanonicalize);
- function.SetRedirectionType(type);
+ if (IsLoaded(type)) {
+ type ^= FinalizeType(cls, type, kCanonicalize);
+ function.SetRedirectionType(type);
+ }
}
} else if (function.IsGetterFunction() ||
function.IsImplicitGetterFunction()) {
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index a4359a7..dd977b2 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -724,6 +724,7 @@
const String& target_name,
const Array& arguments_descriptor,
Function* result) {
+ ASSERT(FLAG_lazy_dispatchers);
// 1. Check if there is a getter with the same name.
const String& getter_name = String::Handle(Field::GetterName(target_name));
const int kNumArguments = 1;
@@ -764,6 +765,10 @@
RawFunction* InlineCacheMissHelper(
const Instance& receiver,
const ICData& ic_data) {
+ if (!FLAG_lazy_dispatchers) {
+ return Function::null(); // We'll handle it in the runtime.
+ }
+
const Array& args_descriptor = Array::Handle(ic_data.arguments_descriptor());
const Class& receiver_class = Class::Handle(receiver.clazz());
@@ -775,9 +780,6 @@
target_name,
args_descriptor,
&result)) {
- if (!FLAG_lazy_dispatchers) {
- return result.raw(); // Return null.
- }
ArgumentsDescriptor desc(args_descriptor);
const Function& target_function =
Function::Handle(receiver_class.GetInvocationDispatcher(
@@ -1042,6 +1044,78 @@
const Array& orig_arguments_desc = Array::CheckedHandle(arguments.ArgAt(2));
const Array& orig_arguments = Array::CheckedHandle(arguments.ArgAt(3));
const String& target_name = String::Handle(ic_data.target_name());
+
+ Class& cls = Class::Handle(receiver.clazz());
+ Function& function = Function::Handle();
+
+ // Dart distinguishes getters and regular methods and allows their calls
+ // to mix with conversions, and its selectors are independent of arity. So do
+ // a zigzagged lookup to see if this call failed because of an arity mismatch,
+ // need for conversion, or there really is no such method.
+
+ const bool is_getter = Field::IsGetterName(target_name);
+ if (is_getter) {
+ // o.foo failed, closurize o.foo() if it exists
+ const String& field_name =
+ String::Handle(Field::NameFromGetter(target_name));
+ while (!cls.IsNull()) {
+ function ^= cls.LookupDynamicFunction(field_name);
+ if (!function.IsNull()) {
+ const Function& closure_function =
+ Function::Handle(function.ImplicitClosureFunction());
+ const Object& result =
+ Object::Handle(closure_function.ImplicitInstanceClosure(receiver));
+ arguments.SetReturn(result);
+ return;
+ }
+ cls = cls.SuperClass();
+ }
+ } else {
+ // o.foo(...) failed, invoke noSuchMethod is foo exists but has the wrong
+ // number of arguments, or try (o.foo).call(...)
+
+ if ((target_name.raw() == Symbols::Call().raw()) && receiver.IsClosure()) {
+ // Special case: closures are implemented with a call getter instead of a
+ // call method and with lazy dispatchers the field-invocation-dispatcher
+ // would perform the closure call.
+ const Object& result =
+ Object::Handle(DartEntry::InvokeClosure(orig_arguments,
+ orig_arguments_desc));
+ CheckResultError(result);
+ arguments.SetReturn(result);
+ return;
+ }
+
+ const String& getter_name = String::Handle(Field::GetterName(target_name));
+ while (!cls.IsNull()) {
+ function ^= cls.LookupDynamicFunction(target_name);
+ if (!function.IsNull()) {
+ ArgumentsDescriptor args_desc(orig_arguments_desc);
+ ASSERT(!function.AreValidArguments(args_desc, NULL));
+ break; // mismatch, invoke noSuchMethod
+ }
+ function ^= cls.LookupDynamicFunction(getter_name);
+ if (!function.IsNull()) {
+ const Array& getter_arguments = Array::Handle(Array::New(1));
+ getter_arguments.SetAt(0, receiver);
+ const Object& getter_result =
+ Object::Handle(DartEntry::InvokeFunction(function,
+ getter_arguments));
+ CheckResultError(getter_result);
+ ASSERT(getter_result.IsNull() || getter_result.IsInstance());
+
+ orig_arguments.SetAt(0, getter_result);
+ const Object& call_result =
+ Object::Handle(DartEntry::InvokeClosure(orig_arguments,
+ orig_arguments_desc));
+ CheckResultError(call_result);
+ arguments.SetReturn(call_result);
+ return;
+ }
+ cls = cls.SuperClass();
+ }
+ }
+
// Handle noSuchMethod invocation.
const Object& result = Object::Handle(
DartEntry::InvokeNoSuchMethod(receiver,
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 885208b..0afc458 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -131,9 +131,9 @@
intptr_t osr_id) {
// Compile to the dart IR.
RegExpEngine::CompilationResult result =
- RegExpEngine::Compile(parsed_function->regexp_compile_data(),
- parsed_function,
- ic_data_array);
+ RegExpEngine::CompileIR(parsed_function->regexp_compile_data(),
+ parsed_function,
+ ic_data_array);
backtrack_goto_ = result.backtrack_goto;
// Allocate variables now that we know the number of locals.
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 9868346..17406ea 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1522,7 +1522,7 @@
isolate->heap()->CollectAllGarbage();
#if defined(DEBUG)
FunctionVisitor check_canonical(isolate);
- isolate->heap()->VisitObjects(&check_canonical);
+ isolate->heap()->IterateObjects(&check_canonical);
#endif // #if defined(DEBUG).
// Since this is only a snapshot the root library should not be set.
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 9a1ea76..817b7e9 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -151,7 +151,50 @@
return InvokeFunction(function, arguments, arguments_descriptor);
}
}
- // There is no compatible 'call' method, so invoke noSuchMethod.
+
+ // There is no compatible 'call' method, see if there's a getter.
+ if (instance.IsClosure()) {
+ // Special case: closures are implemented with a call getter instead of a
+ // call method. If the arguments didn't match, go to noSuchMethod instead
+ // of infinitely recursing on the getter.
+ } else {
+ const String& getter_name = String::Handle(Symbols::New("get:call"));
+ Class& cls = Class::Handle(instance.clazz());
+ while (!cls.IsNull()) {
+ function ^= cls.LookupDynamicFunction(getter_name);
+ if (!function.IsNull()) {
+ // Getters don't have a stack overflow check, so do one in C++.
+
+ Isolate* isolate = Isolate::Current();
+#if defined(USING_SIMULATOR)
+ uword stack_pos = Simulator::Current()->get_register(SPREG);
+#else
+ uword stack_pos = Isolate::GetCurrentStackPointer();
+#endif
+ if (stack_pos < isolate->saved_stack_limit()) {
+ const Instance& exception =
+ Instance::Handle(isolate->object_store()->stack_overflow());
+ return UnhandledException::New(exception, Stacktrace::Handle());
+ }
+
+ const Array& getter_arguments = Array::Handle(Array::New(1));
+ getter_arguments.SetAt(0, instance);
+ const Object& getter_result =
+ Object::Handle(DartEntry::InvokeFunction(function,
+ getter_arguments));
+ if (getter_result.IsError()) {
+ return getter_result.raw();
+ }
+ ASSERT(getter_result.IsNull() || getter_result.IsInstance());
+
+ arguments.SetAt(0, getter_result);
+ return InvokeClosure(arguments, arguments_descriptor);
+ }
+ cls = cls.SuperClass();
+ }
+ }
+
+ // No compatible method or getter so invoke noSuchMethod.
return InvokeNoSuchMethod(instance,
Symbols::Call(),
arguments,
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 0329ca3..abdd58e 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -1811,6 +1811,44 @@
ValueGraphVisitor for_left_value(owner());
node->left()->Visit(&for_left_value);
Append(for_left_value);
+
+ if (!FLAG_warn_on_javascript_compatibility) {
+ if (type.IsNumberType() || type.IsIntType() || type.IsDoubleType() ||
+ type.IsSmiType() || type.IsStringType()) {
+ String& method_name = String::ZoneHandle(Z);
+ if (type.IsNumberType()) {
+ method_name = Symbols::_instanceOfNum().raw();
+ } else if (type.IsIntType()) {
+ method_name = Symbols::_instanceOfInt().raw();
+ } else if (type.IsDoubleType()) {
+ method_name = Symbols::_instanceOfDouble().raw();
+ } else if (type.IsSmiType()) {
+ method_name = Symbols::_instanceOfSmi().raw();
+ } else if (type.IsStringType()) {
+ method_name = Symbols::_instanceOfString().raw();
+ }
+ ASSERT(!method_name.IsNull());
+ PushArgumentInstr* push_left = PushArgument(for_left_value.value());
+ ZoneGrowableArray<PushArgumentInstr*>* arguments =
+ new(Z) ZoneGrowableArray<PushArgumentInstr*>(2);
+ arguments->Add(push_left);
+ const Bool& negate = Bool::Get(node->kind() == Token::kISNOT);
+ Value* negate_arg = Bind(new(Z) ConstantInstr(negate));
+ arguments->Add(PushArgument(negate_arg));
+ const intptr_t kNumArgsChecked = 1;
+ InstanceCallInstr* call = new(Z) InstanceCallInstr(
+ node->token_pos(),
+ Library::PrivateCoreLibName(method_name),
+ node->kind(),
+ arguments,
+ Object::null_array(), // No argument names.
+ kNumArgsChecked,
+ owner()->ic_data_array());
+ ReturnDefinition(call);
+ return;
+ }
+ }
+
PushArgumentInstr* push_left = PushArgument(for_left_value.value());
PushArgumentInstr* push_instantiator = NULL;
PushArgumentInstr* push_type_args = NULL;
@@ -1828,9 +1866,8 @@
arguments->Add(push_instantiator);
arguments->Add(push_type_args);
ASSERT(!node->right()->AsTypeNode()->type().IsNull());
- Value* type_arg = Bind(
- new(Z) ConstantInstr(node->right()->AsTypeNode()->type()));
- arguments->Add(PushArgument(type_arg));
+ Value* type_const = Bind(new(Z) ConstantInstr(type));
+ arguments->Add(PushArgument(type_const));
const Bool& negate = Bool::Get(node->kind() == Token::kISNOT);
Value* negate_arg = Bind(new(Z) ConstantInstr(negate));
arguments->Add(PushArgument(negate_arg));
diff --git a/runtime/vm/flow_graph_compiler_arm.cc b/runtime/vm/flow_graph_compiler_arm.cc
index 5fdb2d8..d169b8d 100644
--- a/runtime/vm/flow_graph_compiler_arm.cc
+++ b/runtime/vm/flow_graph_compiler_arm.cc
@@ -237,7 +237,7 @@
const SubtypeTestCache& type_test_cache =
SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
StubCode* stub_code = isolate()->stub_code();
- __ LoadObject(R2, type_test_cache);
+ __ LoadUniqueObject(R2, type_test_cache);
if (test_kind == kTestTypeOneArg) {
ASSERT(type_arguments_reg == kNoRegister);
__ LoadImmediate(R1, reinterpret_cast<intptr_t>(Object::null()));
@@ -627,7 +627,7 @@
__ PushObject(type); // Push the type.
// Push instantiator (R2) and its type arguments (R1).
__ PushList((1 << R1) | (1 << R2));
- __ LoadObject(R0, test_cache);
+ __ LoadUniqueObject(R0, test_cache);
__ Push(R0);
GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs);
// Pop the parameters supplied to the runtime entry. The result of the
@@ -720,7 +720,7 @@
// Push instantiator (R2) and its type arguments (R1).
__ PushList((1 << R1) | (1 << R2));
__ PushObject(dst_name); // Push the name of the destination.
- __ LoadObject(R0, test_cache);
+ __ LoadUniqueObject(R0, test_cache);
__ Push(R0);
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
// Pop the parameters supplied to the runtime entry. The result of the
@@ -1204,7 +1204,7 @@
const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld));
counter.SetAt(0, Smi::Handle(Smi::New(0)));
__ Comment("Edge counter");
- __ LoadObject(R0, counter);
+ __ LoadUniqueObject(R0, counter);
intptr_t increment_start = assembler_->CodeSize();
#if defined(DEBUG)
bool old_use_far_branches = assembler_->use_far_branches();
@@ -1248,7 +1248,7 @@
// Pass the function explicitly, it is used in IC stub.
__ LoadObject(R6, parsed_function().function());
- __ LoadObject(R5, ic_data);
+ __ LoadUniqueObject(R5, ic_data);
GenerateDartCall(deopt_id,
token_pos,
target_label,
@@ -1265,7 +1265,7 @@
intptr_t token_pos,
LocationSummary* locs) {
ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0);
- __ LoadObject(R5, ic_data);
+ __ LoadUniqueObject(R5, ic_data);
GenerateDartCall(deopt_id,
token_pos,
target_label,
diff --git a/runtime/vm/flow_graph_compiler_arm64.cc b/runtime/vm/flow_graph_compiler_arm64.cc
index e2b8308..f951807 100644
--- a/runtime/vm/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/flow_graph_compiler_arm64.cc
@@ -224,7 +224,7 @@
const SubtypeTestCache& type_test_cache =
SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
StubCode* stub_code = isolate()->stub_code();
- __ LoadObject(R2, type_test_cache, PP);
+ __ LoadUniqueObject(R2, type_test_cache, PP);
if (test_kind == kTestTypeOneArg) {
ASSERT(type_arguments_reg == kNoRegister);
__ LoadObject(R1, Object::null_object(), PP);
@@ -617,7 +617,7 @@
// Push instantiator (R2) and its type arguments (R1).
__ Push(R2);
__ Push(R1);
- __ LoadObject(R0, test_cache, PP);
+ __ LoadUniqueObject(R0, test_cache, PP);
__ Push(R0);
GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs);
// Pop the parameters supplied to the runtime entry. The result of the
@@ -714,7 +714,7 @@
__ Push(R2);
__ Push(R1);
__ PushObject(dst_name, PP); // Push the name of the destination.
- __ LoadObject(R0, test_cache, PP);
+ __ LoadUniqueObject(R0, test_cache, PP);
__ Push(R0);
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
// Pop the parameters supplied to the runtime entry. The result of the
@@ -1207,7 +1207,7 @@
const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld));
counter.SetAt(0, Smi::Handle(Smi::New(0)));
__ Comment("Edge counter");
- __ LoadObject(R0, counter, PP);
+ __ LoadUniqueObject(R0, counter, PP);
__ LoadFieldFromOffset(TMP, R0, Array::element_offset(0), PP);
__ add(TMP, TMP, Operand(Smi::RawValue(1)));
__ StoreFieldToOffset(TMP, R0, Array::element_offset(0), PP);
@@ -1230,7 +1230,7 @@
// Pass the function explicitly, it is used in IC stub.
__ LoadObject(R6, parsed_function().function(), PP);
- __ LoadObject(R5, ic_data, PP);
+ __ LoadUniqueObject(R5, ic_data, PP);
GenerateDartCall(deopt_id,
token_pos,
target_label,
@@ -1247,7 +1247,7 @@
intptr_t token_pos,
LocationSummary* locs) {
ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0);
- __ LoadObject(R5, ic_data, PP);
+ __ LoadUniqueObject(R5, ic_data, PP);
GenerateDartCall(deopt_id,
token_pos,
target_label,
diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc
index 7a59b43..b65b62e 100644
--- a/runtime/vm/flow_graph_compiler_mips.cc
+++ b/runtime/vm/flow_graph_compiler_mips.cc
@@ -227,7 +227,7 @@
const SubtypeTestCache& type_test_cache =
SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
StubCode* stub_code = isolate()->stub_code();
- __ LoadObject(A2, type_test_cache);
+ __ LoadUniqueObject(A2, type_test_cache);
if (test_kind == kTestTypeOneArg) {
ASSERT(type_arguments_reg == kNoRegister);
__ LoadImmediate(A1, reinterpret_cast<int32_t>(Object::null()));
@@ -616,7 +616,7 @@
__ sw(TMP, Address(SP, 3 * kWordSize)); // Push the type.
__ sw(A2, Address(SP, 2 * kWordSize)); // Push instantiator.
__ sw(A1, Address(SP, 1 * kWordSize)); // Push type arguments.
- __ LoadObject(A0, test_cache);
+ __ LoadUniqueObject(A0, test_cache);
__ sw(A0, Address(SP, 0 * kWordSize));
GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs);
// Pop the parameters supplied to the runtime entry. The result of the
@@ -726,7 +726,7 @@
__ sw(A1, Address(SP, 2 * kWordSize)); // Push type arguments.
__ LoadObject(TMP, dst_name);
__ sw(TMP, Address(SP, 1 * kWordSize)); // Push the name of the destination.
- __ LoadObject(T0, test_cache);
+ __ LoadUniqueObject(T0, test_cache);
__ sw(T0, Address(SP, 0 * kWordSize));
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
@@ -1227,7 +1227,7 @@
const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld));
counter.SetAt(0, Smi::Handle(Smi::New(0)));
__ Comment("Edge counter");
- __ LoadObject(T0, counter);
+ __ LoadUniqueObject(T0, counter);
__ lw(T1, FieldAddress(T0, Array::element_offset(0)));
__ AddImmediate(T1, T1, Smi::RawValue(1));
__ sw(T1, FieldAddress(T0, Array::element_offset(0)));
@@ -1250,7 +1250,7 @@
// Pass the function explicitly, it is used in IC stub.
__ Comment("OptimizedInstanceCall");
__ LoadObject(T0, parsed_function().function());
- __ LoadObject(S5, ic_data);
+ __ LoadUniqueObject(S5, ic_data);
GenerateDartCall(deopt_id,
token_pos,
target_label,
@@ -1268,7 +1268,7 @@
LocationSummary* locs) {
ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0);
__ Comment("InstanceCall");
- __ LoadObject(S5, ic_data);
+ __ LoadUniqueObject(S5, ic_data);
GenerateDartCall(deopt_id,
token_pos,
target_label,
diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
index 649e919..3aa6f29 100644
--- a/runtime/vm/flow_graph_compiler_x64.cc
+++ b/runtime/vm/flow_graph_compiler_x64.cc
@@ -225,7 +225,7 @@
const SubtypeTestCache& type_test_cache =
SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
StubCode* stub_code = isolate()->stub_code();
- __ LoadObject(temp_reg, type_test_cache, PP);
+ __ LoadUniqueObject(temp_reg, type_test_cache, PP);
__ pushq(temp_reg); // Subtype test cache.
__ pushq(instance_reg); // Instance.
if (test_kind == kTestTypeOneArg) {
@@ -623,7 +623,7 @@
__ PushObject(type, PP); // Push the type.
__ pushq(RCX); // TODO(srdjan): Pass instantiator instead of null.
__ pushq(RDX); // Instantiator type arguments.
- __ LoadObject(RAX, test_cache, PP);
+ __ LoadUniqueObject(RAX, test_cache, PP);
__ pushq(RAX);
GenerateRuntimeCall(token_pos,
deopt_id,
@@ -720,7 +720,7 @@
__ pushq(RCX); // Instantiator.
__ pushq(RDX); // Instantiator type arguments.
__ PushObject(dst_name, PP); // Push the name of the destination.
- __ LoadObject(RAX, test_cache, PP);
+ __ LoadUniqueObject(RAX, test_cache, PP);
__ pushq(RAX);
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
// Pop the parameters supplied to the runtime entry. The result of the
@@ -1244,7 +1244,7 @@
const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld));
counter.SetAt(0, Smi::Handle(Smi::New(0)));
__ Comment("Edge counter");
- __ LoadObject(RAX, counter, PP);
+ __ LoadUniqueObject(RAX, counter, PP);
intptr_t increment_start = assembler_->CodeSize();
__ IncrementSmiField(FieldAddress(RAX, Array::element_offset(0)), 1);
int32_t size = assembler_->CodeSize() - increment_start;
@@ -1278,7 +1278,7 @@
// reoptimized and which counter needs to be incremented.
// Pass the function explicitly, it is used in IC stub.
__ LoadObject(RDI, parsed_function().function(), PP);
- __ LoadObject(RBX, ic_data, PP);
+ __ LoadUniqueObject(RBX, ic_data, PP);
GenerateDartCall(deopt_id,
token_pos,
target_label,
@@ -1295,7 +1295,7 @@
intptr_t token_pos,
LocationSummary* locs) {
ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0);
- __ LoadObject(RBX, ic_data, PP);
+ __ LoadUniqueObject(RBX, ic_data, PP);
GenerateDartCall(deopt_id,
token_pos,
target_label,
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index abdb548..add12f5 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -4109,12 +4109,40 @@
void FlowGraphOptimizer::ReplaceWithInstanceOf(InstanceCallInstr* call) {
ASSERT(Token::IsTypeTestOperator(call->token_kind()));
Definition* left = call->ArgumentAt(0);
- Definition* instantiator = call->ArgumentAt(1);
- Definition* type_args = call->ArgumentAt(2);
- const AbstractType& type =
- AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value());
- const bool negate = Bool::Cast(
- call->ArgumentAt(4)->OriginalDefinition()->AsConstant()->value()).value();
+ Definition* instantiator = NULL;
+ Definition* type_args = NULL;
+ AbstractType& type = AbstractType::ZoneHandle(Z);
+ bool negate = false;
+ if (call->ArgumentCount() == 2) {
+ instantiator = flow_graph()->constant_null();
+ type_args = flow_graph()->constant_null();
+ if (call->function_name().raw() ==
+ Library::PrivateCoreLibName(Symbols::_instanceOfNum()).raw()) {
+ type = Type::Number();
+ } else if (call->function_name().raw() ==
+ Library::PrivateCoreLibName(Symbols::_instanceOfInt()).raw()) {
+ type = Type::IntType();
+ } else if (call->function_name().raw() ==
+ Library::PrivateCoreLibName(Symbols::_instanceOfSmi()).raw()) {
+ type = Type::SmiType();
+ } else if (call->function_name().raw() ==
+ Library::PrivateCoreLibName(Symbols::_instanceOfDouble()).raw()) {
+ type = Type::Double();
+ } else if (call->function_name().raw() ==
+ Library::PrivateCoreLibName(Symbols::_instanceOfString()).raw()) {
+ type = Type::StringType();
+ } else {
+ UNIMPLEMENTED();
+ }
+ negate = Bool::Cast(call->ArgumentAt(1)->OriginalDefinition()
+ ->AsConstant()->value()).value();
+ } else {
+ instantiator = call->ArgumentAt(1);
+ type_args = call->ArgumentAt(2);
+ type = AbstractType::Cast(call->ArgumentAt(3)->AsConstant()->value()).raw();
+ negate = Bool::Cast(call->ArgumentAt(4)->OriginalDefinition()
+ ->AsConstant()->value()).value();
+ }
const ICData& unary_checks =
ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks());
if (FLAG_warn_on_javascript_compatibility &&
diff --git a/runtime/vm/heap.cc b/runtime/vm/heap.cc
index 27d39d3..e66d6db 100644
--- a/runtime/vm/heap.cc
+++ b/runtime/vm/heap.cc
@@ -219,6 +219,50 @@
}
+HeapIterationScope::HeapIterationScope()
+ : StackResource(Thread::Current()->isolate()),
+ old_space_(isolate()->heap()->old_space()) {
+ // It's not yet safe to iterate over a paged space while it's concurrently
+ // sweeping, so wait for any such task to complete first.
+ MonitorLocker ml(old_space_->tasks_lock());
+#if defined(DEBUG)
+ // We currently don't support nesting of HeapIterationScopes.
+ ASSERT(!old_space_->is_iterating_);
+ old_space_->is_iterating_ = true;
+#endif
+ while (old_space_->tasks() > 0) {
+ ml.Wait();
+ }
+ old_space_->set_tasks(1);
+}
+
+
+HeapIterationScope::~HeapIterationScope() {
+ MonitorLocker ml(old_space_->tasks_lock());
+#if defined(DEBUG)
+ ASSERT(old_space_->is_iterating_);
+ old_space_->is_iterating_ = false;
+#endif
+ ASSERT(old_space_->tasks() == 1);
+ old_space_->set_tasks(0);
+ ml.Notify();
+}
+
+
+void Heap::IterateObjects(ObjectVisitor* visitor) const {
+ // The visitor must not allocate from the heap.
+ NoSafepointScope no_safepoint_scope_;
+ new_space_->VisitObjects(visitor);
+ IterateOldObjects(visitor);
+}
+
+
+void Heap::IterateOldObjects(ObjectVisitor* visitor) const {
+ HeapIterationScope heap_iteration_scope;
+ old_space_->VisitObjects(visitor);
+}
+
+
void Heap::VisitObjectPointers(ObjectPointerVisitor* visitor) const {
new_space_->VisitObjectPointers(visitor);
old_space_->VisitObjectPointers(visitor);
@@ -235,11 +279,7 @@
RawObject* Heap::FindOldObject(FindObjectVisitor* visitor) const {
- // Wait for any concurrent GC tasks to finish before walking.
- MonitorLocker ml(old_space_->tasks_lock());
- while (old_space_->tasks() > 0) {
- ml.Wait();
- }
+ HeapIterationScope heap_iteration_scope;
return old_space_->FindObject(visitor, HeapPage::kData);
}
@@ -250,7 +290,8 @@
RawObject* Heap::FindObject(FindObjectVisitor* visitor) const {
- ASSERT(isolate()->no_safepoint_scope_depth() != 0);
+ // The visitor must not allocate from the heap.
+ NoSafepointScope no_safepoint_scope;
RawObject* raw_obj = FindNewObject(visitor);
if (raw_obj != Object::null()) {
return raw_obj;
@@ -489,6 +530,12 @@
bool Heap::Verify(MarkExpectation mark_expectation) const {
+ HeapIterationScope heap_iteration_scope;
+ return VerifyGC(mark_expectation);
+}
+
+
+bool Heap::VerifyGC(MarkExpectation mark_expectation) const {
ObjectSet* allocated_set = CreateAllocatedObjectSet(mark_expectation);
VerifyPointersVisitor visitor(isolate(), allocated_set);
VisitObjectPointers(&visitor);
diff --git a/runtime/vm/heap.h b/runtime/vm/heap.h
index 6d8752c..4385883 100644
--- a/runtime/vm/heap.h
+++ b/runtime/vm/heap.h
@@ -105,14 +105,9 @@
bool CodeContains(uword addr) const;
bool StubCodeContains(uword addr) const;
- // Visit all pointers. Caller must ensure concurrent sweeper is not running,
- // and the visitor must not allocate (see issue 21620).
- void VisitObjectPointers(ObjectPointerVisitor* visitor) const;
-
- // Visit all objects, including FreeListElement "objects". Caller must ensure
- // concurrent sweeper is not running, and the visitor must not allocate (see
- // issue 21620).
- void VisitObjects(ObjectVisitor* visitor) const;
+ void IterateObjects(ObjectVisitor* visitor) const;
+ void IterateOldObjects(ObjectVisitor* visitor) const;
+ void IterateObjectPointers(ObjectVisitor* visitor) const;
// Find an object by visiting all pointers in the specified heap space,
// the 'visitor' is used to determine if an object is found or not.
@@ -121,8 +116,7 @@
// point.
// The 'visitor' function should return false if the object is not found,
// traversal through the heap space continues.
- // Returns null object if nothing is found. Must be called within a
- // NoSafepointScope.
+ // Returns null object if nothing is found.
RawInstructions* FindObjectInCodeSpace(FindObjectVisitor* visitor) const;
RawObject* FindOldObject(FindObjectVisitor* visitor) const;
RawObject* FindNewObject(FindObjectVisitor* visitor) const;
@@ -284,6 +278,18 @@
uword AllocateOld(intptr_t size, HeapPage::PageType type);
uword AllocatePretenured(intptr_t size);
+ // Visit all pointers. Caller must ensure concurrent sweeper is not running,
+ // and the visitor must not allocate.
+ void VisitObjectPointers(ObjectPointerVisitor* visitor) const;
+
+ // Visit all objects, including FreeListElement "objects". Caller must ensure
+ // concurrent sweeper is not running, and the visitor must not allocate.
+ void VisitObjects(ObjectVisitor* visitor) const;
+
+ // Like Verify, but does not wait for concurrent sweeper, so caller must
+ // ensure thread-safety.
+ bool VerifyGC(MarkExpectation mark_expectation = kForbidMarked) const;
+
// GC stats collection.
void RecordBeforeGC(Space space, GCReason reason);
void RecordAfterGC();
@@ -318,6 +324,7 @@
friend class ServiceEvent;
friend class GCTestHelper;
+ friend class PageSpace; // VerifyGC
DISALLOW_COPY_AND_ASSIGN(Heap);
};
@@ -342,6 +349,18 @@
#endif // defined(DEBUG)
+class HeapIterationScope : public StackResource {
+ public:
+ HeapIterationScope();
+ ~HeapIterationScope();
+ private:
+ NoSafepointScope no_safepoint_scope_;
+ PageSpace* old_space_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapIterationScope);
+};
+
+
class NoHeapGrowthControlScope : public StackResource {
public:
NoHeapGrowthControlScope();
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index cc17570..a5e7f5e 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -18,7 +18,7 @@
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp_assembler_ir.h"
#include "vm/resolver.h"
#include "vm/scopes.h"
#include "vm/stub_code.h"
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 4ba8fed..6238014 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -18,6 +18,8 @@
namespace dart {
+DECLARE_FLAG(bool, interpret_irregexp);
+
// When entering intrinsics code:
// R5: IC Data
// R4: Arguments descriptor
@@ -1570,6 +1572,35 @@
}
+// Return type quickly for simple types (not parameterized and not signature).
+void Intrinsifier::ObjectRuntimeType(Assembler* assembler) {
+ Label fall_through;
+ static const intptr_t kSmiCidSource = kSmiCid << RawObject::kClassIdTagPos;
+ __ ldr(R0, Address(SP, 0 * kWordSize));
+
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(&kSmiCidSource) + 1);
+ __ tst(R0, Operand(kSmiTagMask));
+ __ mov(TMP, Operand(R0), NE);
+ __ LoadClassId(R1, TMP);
+ __ LoadClassById(R2, R1);
+ // R2: class of instance (R0).
+ __ ldr(R3, FieldAddress(R2, Class::signature_function_offset()));
+ __ CompareImmediate(R3, reinterpret_cast<int32_t>(Object::null()));
+ __ b(&fall_through, NE);
+
+ __ ldrh(R3, FieldAddress(R2, Class::num_type_arguments_offset()));
+ __ CompareImmediate(R3, 0);
+ __ b(&fall_through, NE);
+
+ __ ldr(R0, FieldAddress(R2, Class::canonical_types_offset()));
+ __ CompareImmediate(R0, reinterpret_cast<int32_t>(Object::null()));
+ __ b(&fall_through, EQ);
+ __ Ret();
+
+ __ Bind(&fall_through);
+}
+
+
void Intrinsifier::String_getHashCode(Assembler* assembler) {
__ ldr(R0, Address(SP, 0 * kWordSize));
__ ldr(R0, FieldAddress(R0, String::hash_offset()));
@@ -1962,6 +1993,8 @@
void Intrinsifier::JSRegExp_ExecuteMatch(Assembler* assembler) {
+ if (FLAG_interpret_irregexp) return;
+
static const intptr_t kRegExpParamOffset = 2 * kWordSize;
static const intptr_t kStringParamOffset = 1 * kWordSize;
// start_index smi is located at offset 0.
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index fedbe17..dd46bcc 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -17,6 +17,8 @@
namespace dart {
+DECLARE_FLAG(bool, interpret_irregexp);
+
// When entering intrinsics code:
// R5: IC Data
// R4: Arguments descriptor
@@ -1646,6 +1648,30 @@
}
+// Return type quickly for simple types (not parameterized and not signature).
+void Intrinsifier::ObjectRuntimeType(Assembler* assembler) {
+ Label fall_through;
+ __ ldr(R0, Address(SP, 0 * kWordSize));
+ __ LoadClassIdMayBeSmi(R1, R0);
+ __ LoadClassById(R2, R1, PP);
+ // R2: class of instance (R0).
+ __ ldr(R3, FieldAddress(R2, Class::signature_function_offset()));
+ __ CompareObject(R3, Object::null_object(), PP);
+ __ b(&fall_through, NE);
+
+ __ ldr(R3, FieldAddress(R2, Class::num_type_arguments_offset()), kHalfword);
+ __ CompareImmediate(R3, 0, kNoPP);
+ __ b(&fall_through, NE);
+
+ __ ldr(R0, FieldAddress(R2, Class::canonical_types_offset()));
+ __ CompareObject(R0, Object::null_object(), PP);
+ __ b(&fall_through, EQ);
+ __ ret();
+
+ __ Bind(&fall_through);
+}
+
+
void Intrinsifier::String_getHashCode(Assembler* assembler) {
Label fall_through;
__ ldr(R0, Address(SP, 0 * kWordSize));
@@ -2043,6 +2069,8 @@
void Intrinsifier::JSRegExp_ExecuteMatch(Assembler* assembler) {
+ if (FLAG_interpret_irregexp) return;
+
static const intptr_t kRegExpParamOffset = 2 * kWordSize;
static const intptr_t kStringParamOffset = 1 * kWordSize;
// start_index smi is located at offset 0.
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index 93290ed..2b15a71 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -24,6 +24,8 @@
namespace dart {
+DECLARE_FLAG(bool, interpret_irregexp);
+
// When entering intrinsics code:
// ECX: IC Data
// EDX: Arguments descriptor
@@ -1666,6 +1668,31 @@
}
+// Return type quickly for simple types (not parameterized and not signature).
+void Intrinsifier::ObjectRuntimeType(Assembler* assembler) {
+ Label fall_through;
+ __ movl(EAX, Address(ESP, + 1 * kWordSize));
+ __ LoadClassIdMayBeSmi(EDI, EAX);
+ __ LoadClassById(EBX, EDI);
+ // EBX: class of instance (EAX).
+ const Immediate& raw_null =
+ Immediate(reinterpret_cast<intptr_t>(Object::null()));
+ __ movl(EDI, FieldAddress(EBX, Class::signature_function_offset()));
+ __ cmpl(EDI, raw_null);
+ __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
+
+ __ movzxw(EDI, FieldAddress(EBX, Class::num_type_arguments_offset()));
+ __ cmpl(EDI, Immediate(0));
+ __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
+ __ movl(EAX, FieldAddress(EBX, Class::canonical_types_offset()));
+ __ cmpl(EAX, raw_null);
+ __ j(EQUAL, &fall_through, Assembler::kNearJump); // Not yet set.
+ __ ret();
+
+ __ Bind(&fall_through);
+}
+
+
void Intrinsifier::String_getHashCode(Assembler* assembler) {
Label fall_through;
__ movl(EAX, Address(ESP, + 1 * kWordSize)); // String object.
@@ -2052,6 +2079,8 @@
void Intrinsifier::JSRegExp_ExecuteMatch(Assembler* assembler) {
+ if (FLAG_interpret_irregexp) return;
+
static const intptr_t kRegExpParamOffset = 3 * kWordSize;
static const intptr_t kStringParamOffset = 2 * kWordSize;
// start_index smi is located at offset 1.
@@ -2073,7 +2102,7 @@
// Registers are now set up for the lazy compile stub. It expects the function
// in EAX, the argument descriptor in EDX, and IC-Data in ECX.
static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
- __ LoadObject(EDX, Array::Handle(ArgumentsDescriptor::New(arg_count)));
+ __ LoadObject(EDX, Array::ZoneHandle(ArgumentsDescriptor::New(arg_count)));
__ xorl(ECX, ECX);
// Tail-call the function.
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index ecf07e6..e20cbd0 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -17,6 +17,8 @@
namespace dart {
+DECLARE_FLAG(bool, interpret_irregexp);
+
// When entering intrinsics code:
// S5: IC Data
// S4: Arguments descriptor
@@ -1675,6 +1677,28 @@
}
+// Return type quickly for simple types (not parameterized and not signature).
+void Intrinsifier::ObjectRuntimeType(Assembler* assembler) {
+ Label fall_through;
+ __ lw(T0, Address(SP, 0 * kWordSize));
+ __ LoadClassIdMayBeSmi(T1, T0);
+ __ LoadClassById(T2, T1);
+ // T2: class of instance (T0).
+
+ __ lw(T1, FieldAddress(T2, Class::signature_function_offset()));
+ __ BranchNotEqual(T1, Object::null_object(), &fall_through);
+
+ __ lhu(T1, FieldAddress(T2, Class::num_type_arguments_offset()));
+ __ BranchNotEqual(T1, Immediate(0), &fall_through);
+
+ __ lw(V0, FieldAddress(T2, Class::canonical_types_offset()));
+ __ BranchEqual(V0, Object::null_object(), &fall_through);
+ __ Ret();
+
+ __ Bind(&fall_through);
+}
+
+
void Intrinsifier::String_getHashCode(Assembler* assembler) {
Label fall_through;
__ lw(T0, Address(SP, 0 * kWordSize));
@@ -2075,6 +2099,8 @@
void Intrinsifier::JSRegExp_ExecuteMatch(Assembler* assembler) {
+ if (FLAG_interpret_irregexp) return;
+
static const intptr_t kRegExpParamOffset = 2 * kWordSize;
static const intptr_t kStringParamOffset = 1 * kWordSize;
// start_index smi is located at 0.
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index 14eb3cf..96fe7bc 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -17,6 +17,8 @@
namespace dart {
+DECLARE_FLAG(bool, interpret_irregexp);
+
// When entering intrinsics code:
// RBX: IC Data
// R10: Arguments descriptor
@@ -1524,6 +1526,31 @@
}
+// Return type quickly for simple types (not parameterized and not signature).
+void Intrinsifier::ObjectRuntimeType(Assembler* assembler) {
+ Label fall_through;
+ __ movq(RAX, Address(RSP, + 1 * kWordSize));
+ __ LoadClassIdMayBeSmi(RCX, RAX);
+
+ // RCX: untagged cid of instance (RAX).
+ __ LoadClassById(RDI, RCX, PP);
+ // RDI: class of instance (RAX).
+ __ movq(RCX, FieldAddress(RDI, Class::signature_function_offset()));
+ __ CompareObject(RCX, Object::null_object(), PP);
+ __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
+
+ __ movzxw(RCX, FieldAddress(RDI, Class::num_type_arguments_offset()));
+ __ cmpq(RCX, Immediate(0));
+ __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
+ __ movq(RAX, FieldAddress(RDI, Class::canonical_types_offset()));
+ __ CompareObject(RAX, Object::null_object(), PP);
+ __ j(EQUAL, &fall_through, Assembler::kNearJump); // Not yet set.
+ __ ret();
+
+ __ Bind(&fall_through);
+}
+
+
void Intrinsifier::String_getHashCode(Assembler* assembler) {
Label fall_through;
__ movq(RAX, Address(RSP, + 1 * kWordSize)); // String object.
@@ -1911,6 +1938,8 @@
void Intrinsifier::JSRegExp_ExecuteMatch(Assembler* assembler) {
+ if (FLAG_interpret_irregexp) return;
+
static const intptr_t kRegExpParamOffset = 3 * kWordSize;
static const intptr_t kStringParamOffset = 2 * kWordSize;
// start_index smi is located at offset 1.
@@ -1932,7 +1961,8 @@
// Registers are now set up for the lazy compile stub. It expects the function
// in RAX, the argument descriptor in R10, and IC-Data in RCX.
static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
- __ LoadObject(R10, Array::Handle(ArgumentsDescriptor::New(arg_count)), PP);
+ __ LoadObject(R10,
+ Array::ZoneHandle(ArgumentsDescriptor::New(arg_count)), PP);
__ xorq(RCX, RCX);
// Tail-call the function.
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 195623b..50df955 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1426,12 +1426,6 @@
ASSERT(top_resource() == NULL);
#if defined(DEBUG)
if (heap_ != NULL) {
- // Wait for concurrent GC tasks to finish before final verification.
- PageSpace* old_space = heap_->old_space();
- MonitorLocker ml(old_space->tasks_lock());
- while (old_space->tasks() > 0) {
- ml.Wait();
- }
// The VM isolate keeps all objects marked.
heap_->Verify(this == Dart::vm_isolate() ? kRequireMarked : kForbidMarked);
}
@@ -1513,6 +1507,14 @@
Isolate* Isolate::isolates_list_head_ = NULL;
+void Isolate::IterateObjectPointers(ObjectPointerVisitor* visitor,
+ bool visit_prologue_weak_handles,
+ bool validate_frames) {
+ HeapIterationScope heap_iteration_scope;
+ VisitObjectPointers(visitor, visit_prologue_weak_handles, validate_frames);
+}
+
+
void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor,
bool visit_prologue_weak_handles,
bool validate_frames) {
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 2b1256f..447afac 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -133,9 +133,9 @@
void ValidateClassTable();
// Visit all object pointers.
- void VisitObjectPointers(ObjectPointerVisitor* visitor,
- bool visit_prologue_weak_persistent_handles,
- bool validate_frames);
+ void IterateObjectPointers(ObjectPointerVisitor* visitor,
+ bool visit_prologue_weak_persistent_handles,
+ bool validate_frames);
// Visits weak object pointers.
void VisitWeakPersistentHandles(HandleVisitor* visitor,
@@ -727,6 +727,12 @@
void ProfileIdle();
+ // Visit all object pointers. Caller must ensure concurrent sweeper is not
+ // running, and the visitor must not allocate.
+ void VisitObjectPointers(ObjectPointerVisitor* visitor,
+ bool visit_prologue_weak_persistent_handles,
+ bool validate_frames);
+
void set_user_tag(uword tag) {
user_tag_ = tag;
}
@@ -882,6 +888,8 @@
REUSABLE_HANDLE_LIST(REUSABLE_FRIEND_DECLARATION)
#undef REUSABLE_FRIEND_DECLARATION
+ friend class GCMarker; // VisitObjectPointers
+ friend class Scavenger; // VisitObjectPointers
friend class ServiceIsolate;
friend class Thread;
diff --git a/runtime/vm/method_recognizer.h b/runtime/vm/method_recognizer.h
index cc3ef7d..eb752f9 100644
--- a/runtime/vm/method_recognizer.h
+++ b/runtime/vm/method_recognizer.h
@@ -190,6 +190,7 @@
V(_GrowableList, add, GrowableArray_add, 1675959698) \
V(_JSSyntaxRegExp, _ExecuteMatch, JSRegExp_ExecuteMatch, 1711509198) \
V(Object, ==, ObjectEquals, 409406570) \
+ V(Object, get:runtimeType, ObjectRuntimeType, 2076963579) \
V(_StringBase, get:hashCode, String_getHashCode, 2103025405) \
V(_StringBase, get:isEmpty, StringBaseIsEmpty, 780870414) \
V(_StringBase, codeUnitAt, StringBaseCodeUnitAt, 397735324) \
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 150be3e..907156f 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -876,7 +876,7 @@
PremarkingVisitor premarker(isolate);
isolate->heap()->WriteProtect(false);
ASSERT(isolate->heap()->UsedInWords(Heap::kNew) == 0);
- isolate->heap()->old_space()->VisitObjects(&premarker);
+ isolate->heap()->IterateOldObjects(&premarker);
isolate->heap()->WriteProtect(true);
}
@@ -3551,6 +3551,7 @@
return raw_ptr()->canonical_types_;
}
+
void Class::set_canonical_types(const Object& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->canonical_types_, value.raw());
@@ -6511,6 +6512,21 @@
}
+RawInstance* Function::ImplicitInstanceClosure(const Instance& receiver) const {
+ ASSERT(IsImplicitClosureFunction());
+ const Class& cls = Class::Handle(signature_class());
+ const Context& context = Context::Handle(Context::New(1));
+ context.SetAt(0, receiver);
+ const Instance& result = Instance::Handle(Closure::New(*this, context));
+ if (cls.NumTypeArguments() > 0) {
+ const TypeArguments& type_arguments =
+ TypeArguments::Handle(receiver.GetTypeArguments());
+ result.SetTypeArguments(type_arguments);
+ }
+ return result.raw();
+}
+
+
RawString* Function::BuildSignature(bool instantiate,
NameVisibility name_visibility,
const TypeArguments& instantiator) const {
@@ -14882,6 +14898,12 @@
}
+bool AbstractType::IsSmiType() const {
+ return HasResolvedTypeClass() &&
+ (type_class() == Type::Handle(Type::SmiType()).type_class());
+}
+
+
bool AbstractType::IsStringType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::StringType()).type_class());
@@ -20820,6 +20842,15 @@
}
+void JSRegExp::set_bytecode(bool is_one_byte, const TypedData& bytecode) const {
+ if (is_one_byte) {
+ StorePointer(&raw_ptr()->one_byte_bytecode_, bytecode.raw());
+ } else {
+ StorePointer(&raw_ptr()->two_byte_bytecode_, bytecode.raw());
+ }
+}
+
+
void JSRegExp::set_num_bracket_expressions(intptr_t value) const {
StoreSmi(&raw_ptr()->num_bracket_expressions_, Smi::New(value));
}
@@ -20835,6 +20866,7 @@
result ^= raw;
result.set_type(kUnitialized);
result.set_flags(0);
+ result.set_num_registers(-1);
}
return result.raw();
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index ad21c1a..b259aac 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1049,6 +1049,9 @@
}
return reinterpret_cast<RawType*>(Object::null());
}
+ static intptr_t canonical_types_offset() {
+ return OFFSET_OF(RawClass, canonical_types_);
+ }
// The super type of this class, Object type if not explicitly specified.
// Note that the super type may be bounded, as in this example:
@@ -1442,6 +1445,9 @@
return raw_ptr()->num_type_arguments_;
}
void set_num_type_arguments(intptr_t value) const;
+ static intptr_t num_type_arguments_offset() {
+ return OFFSET_OF(RawClass, num_type_arguments_);
+ }
int16_t num_own_type_arguments() const {
return raw_ptr()->num_own_type_arguments_;
@@ -1484,6 +1490,7 @@
friend class Instance;
friend class Object;
friend class Type;
+ friend class Intrinsifier;
};
@@ -2190,6 +2197,8 @@
// If none exists yet, create one and remember it.
RawInstance* ImplicitStaticClosure() const;
+ RawInstance* ImplicitInstanceClosure(const Instance& receiver) const;
+
// Redirection information for a redirecting factory.
bool IsRedirectingFactory() const;
RawType* RedirectionType() const;
@@ -5030,6 +5039,9 @@
// Check if this type represents the 'num' type.
bool IsNumberType() const;
+ // Check if this type represents the '_Smi' type.
+ bool IsSmiType() const;
+
// Check if this type represents the 'String' type.
bool IsStringType() const;
@@ -7658,11 +7670,18 @@
bool is_ignore_case() const { return (flags() & kIgnoreCase); }
bool is_multi_line() const { return (flags() & kMultiLine); }
+ intptr_t num_registers() const { return raw_ptr()->num_registers_; }
+
RawString* pattern() const { return raw_ptr()->pattern_; }
RawSmi* num_bracket_expressions() const {
return raw_ptr()->num_bracket_expressions_;
}
+ RawTypedData* bytecode(bool is_one_byte) const {
+ return is_one_byte ? raw_ptr()->one_byte_bytecode_
+ : raw_ptr()->two_byte_bytecode_;
+ }
+
static intptr_t function_offset(intptr_t cid) {
switch (cid) {
case kOneByteStringCid:
@@ -7690,6 +7709,7 @@
void set_pattern(const String& pattern) const;
void set_function(intptr_t cid, const Function& value) const;
+ void set_bytecode(bool is_one_byte, const TypedData& bytecode) const;
void set_num_bracket_expressions(intptr_t value) const;
void set_is_global() const { set_flags(flags() | kGlobal); }
@@ -7697,6 +7717,9 @@
void set_is_multi_line() const { set_flags(flags() | kMultiLine); }
void set_is_simple() const { set_type(kSimple); }
void set_is_complex() const { set_type(kComplex); }
+ void set_num_registers(intptr_t value) const {
+ StoreNonPointer(&raw_ptr()->num_registers_, value);
+ }
void* GetDataStartAddress() const;
static RawJSRegExp* FromDataStartAddress(void* data);
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index b07fe0a..2e30f25 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -148,7 +148,7 @@
static void UnmarkAll(Isolate* isolate) {
Unmarker unmarker(isolate);
- isolate->heap()->VisitObjects(&unmarker);
+ isolate->heap()->IterateObjects(&unmarker);
}
private:
@@ -172,13 +172,8 @@
void ObjectGraph::IterateObjects(ObjectGraph::Visitor* visitor) {
NoSafepointScope no_safepoint_scope_;
- PageSpace* old_space = isolate()->heap()->old_space();
- MonitorLocker ml(old_space->tasks_lock());
- while (old_space->tasks() > 0) {
- ml.Wait();
- }
Stack stack(isolate());
- isolate()->VisitObjectPointers(&stack, false, false);
+ isolate()->IterateObjectPointers(&stack, false, false);
stack.TraverseGraph(visitor);
Unmarker::UnmarkAll(isolate());
}
@@ -187,11 +182,6 @@
void ObjectGraph::IterateObjectsFrom(const Object& root,
ObjectGraph::Visitor* visitor) {
NoSafepointScope no_safepoint_scope_;
- PageSpace* old_space = isolate()->heap()->old_space();
- MonitorLocker ml(old_space->tasks_lock());
- while (old_space->tasks() > 0) {
- ml.Wait();
- }
Stack stack(isolate());
RawObject* root_raw = root.raw();
stack.VisitPointer(&root_raw);
@@ -377,7 +367,7 @@
Object& scratch = Object::Handle();
NoSafepointScope no_safepoint_scope_;
InboundReferencesVisitor visitor(isolate(), obj->raw(), references, &scratch);
- isolate()->heap()->VisitObjects(&visitor);
+ isolate()->heap()->IterateObjects(&visitor);
return visitor.length();
}
@@ -466,7 +456,7 @@
stream->WriteUnsigned(0);
{
WritePointerVisitor ptr_writer(isolate(), stream);
- isolate()->VisitObjectPointers(&ptr_writer, false, false);
+ isolate()->IterateObjectPointers(&ptr_writer, false, false);
}
stream->WriteUnsigned(0);
IterateObjects(&visitor);
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 028455d..156fb48 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4248,19 +4248,9 @@
TEST_CASE(PrintJSON) {
Heap* heap = Isolate::Current()->heap();
heap->CollectAllGarbage();
- // We don't want to print garbage objects, so wait for concurrent sweeper.
- // TODO(21620): Add heap iteration interface that excludes garbage (or
- // use ObjectGraph).
- PageSpace* old_space = heap->old_space();
- {
- MonitorLocker ml(old_space->tasks_lock());
- while (old_space->tasks() > 0) {
- ml.Wait();
- }
- }
GrowableArray<Object*> objects;
ObjectAccumulator acc(&objects);
- heap->VisitObjects(&acc);
+ heap->IterateObjects(&acc);
for (intptr_t i = 0; i < objects.length(); ++i) {
JSONStream js;
objects[i]->PrintJSON(&js, false);
diff --git a/runtime/vm/pages.cc b/runtime/vm/pages.cc
index 18eec36..109df50 100644
--- a/runtime/vm/pages.cc
+++ b/runtime/vm/pages.cc
@@ -160,6 +160,9 @@
max_external_in_words_(max_external_in_words),
tasks_lock_(new Monitor()),
tasks_(0),
+#if defined(DEBUG)
+ is_iterating_(false),
+#endif
page_space_controller_(heap,
FLAG_old_gen_growth_space_ratio,
FLAG_old_gen_growth_rate,
@@ -778,7 +781,7 @@
if (FLAG_verify_before_gc) {
OS::PrintErr("Verifying before marking...");
- heap_->Verify();
+ heap_->VerifyGC();
OS::PrintErr(" done.\n");
}
@@ -810,7 +813,7 @@
{
if (FLAG_verify_before_gc) {
OS::PrintErr("Verifying before sweeping...");
- heap_->Verify(kAllowMarked);
+ heap_->VerifyGC(kAllowMarked);
OS::PrintErr(" done.\n");
}
GCSweeper sweeper;
@@ -871,7 +874,7 @@
}
if (FLAG_verify_after_gc) {
OS::PrintErr("Verifying after sweeping...");
- heap_->Verify(kForbidMarked);
+ heap_->VerifyGC(kForbidMarked);
OS::PrintErr(" done.\n");
}
} else {
diff --git a/runtime/vm/pages.h b/runtime/vm/pages.h
index 0d0d4dd..cd2b8cb 100644
--- a/runtime/vm/pages.h
+++ b/runtime/vm/pages.h
@@ -424,7 +424,9 @@
// Keep track of running MarkSweep tasks.
Monitor* tasks_lock_;
intptr_t tasks_;
-
+#if defined(DEBUG)
+ bool is_iterating_;
+#endif
PageSpaceController page_space_controller_;
int64_t gc_time_micros_;
@@ -433,6 +435,7 @@
friend class ExclusivePageIterator;
friend class ExclusiveCodePageIterator;
friend class ExclusiveLargePageIterator;
+ friend class HeapIterationScope;
friend class PageSpaceController;
friend class SweeperTask;
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 6cc8e54..a966b40 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -46,6 +46,7 @@
DECLARE_FLAG(bool, warn_on_javascript_compatibility);
DEFINE_FLAG(bool, enable_mirrors, true,
"Disable to make importing dart:mirrors an error.");
+DECLARE_FLAG(bool, lazy_dispatchers);
// Quick access to the current isolate and zone.
#define I (isolate())
@@ -1330,6 +1331,8 @@
SequenceNode* Parser::ParseMethodExtractor(const Function& func) {
TRACE_PARSER("ParseMethodExtractor");
+ ASSERT(FLAG_lazy_dispatchers);
+
ParamList params;
const intptr_t ident_pos = func.token_pos();
@@ -1402,6 +1405,7 @@
SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func,
Array* default_values) {
TRACE_PARSER("ParseNoSuchMethodDispatcher");
+ ASSERT(FLAG_lazy_dispatchers);
ASSERT(func.IsNoSuchMethodDispatcher());
intptr_t token_pos = func.token_pos();
@@ -1459,6 +1463,7 @@
SequenceNode* Parser::ParseInvokeFieldDispatcher(const Function& func,
Array* default_values) {
TRACE_PARSER("ParseInvokeFieldDispatcher");
+ ASSERT(FLAG_lazy_dispatchers);
ASSERT(func.IsInvokeFieldDispatcher());
intptr_t token_pos = func.token_pos();
@@ -3573,7 +3578,7 @@
(LookaheadToken(3) == Token::kPERIOD);
const AbstractType& type = AbstractType::Handle(Z,
ParseType(ClassFinalizer::kResolveTypeParameters,
- false, // Deferred types not allowed.
+ true,
consume_unresolved_prefix));
if (!type.IsMalformed() && type.IsTypeParameter()) {
// Replace the type with a malformed type and compile a throw when called.
@@ -12019,13 +12024,16 @@
// If deferred prefixes are allowed but it is not yet loaded,
// remember that this function depends on the prefix.
if (allow_deferred_type && !prefix.is_loaded()) {
- ASSERT(parsed_function() != NULL);
- parsed_function()->AddDeferredPrefix(prefix);
+ if (parsed_function() != NULL) {
+ parsed_function()->AddDeferredPrefix(prefix);
+ }
}
- // If the deferred prefixes are not allowed, or if the prefix
- // is not yet loaded, return a malformed type. Otherwise, handle
- // resolution below, as needed.
- if (!prefix.is_loaded() || !allow_deferred_type) {
+ // If the deferred prefixes are not allowed, or if the prefix is not yet
+ // loaded when finalization is requested, return a malformed type.
+ // Otherwise, handle resolution below, as needed.
+ if (!allow_deferred_type ||
+ (!prefix.is_loaded()
+ && (finalization > ClassFinalizer::kResolveTypeParameters))) {
ParseTypeArguments(ClassFinalizer::kIgnore);
return ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
@@ -12702,6 +12710,28 @@
String::Handle(Z, redirect_type.UserVisibleName()).ToCString());
}
}
+ if (!redirect_type.HasResolvedTypeClass()) {
+ // If the redirection type is unresolved, we convert the allocation
+ // into throwing a type error.
+ const UnresolvedClass& cls =
+ UnresolvedClass::Handle(Z, redirect_type.unresolved_class());
+ const LibraryPrefix& prefix =
+ LibraryPrefix::Handle(Z, cls.library_prefix());
+ if (!prefix.IsNull() && !prefix.is_loaded()) {
+ // If the redirection type is unresolved because it refers to
+ // an unloaded deferred prefix, mark this function as depending
+ // on the library prefix. It will then get invalidated when the
+ // prefix is loaded.
+ parsed_function()->AddDeferredPrefix(prefix);
+ }
+ redirect_type = ClassFinalizer::NewFinalizedMalformedType(
+ Error::Handle(Z),
+ script_,
+ call_pos,
+ "redirection type '%s' is not loaded",
+ String::Handle(Z, redirect_type.UserVisibleName()).ToCString());
+ }
+
if (redirect_type.IsMalformedOrMalbounded()) {
if (is_const) {
ReportError(Error::Handle(Z, redirect_type.error()));
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 3be710b..cc967e7 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -638,7 +638,7 @@
int32_t type_arguments_field_offset_in_words_; // Offset of type args fld.
int32_t next_field_offset_in_words_; // Offset of the next instance field.
classid_t id_; // Class Id, also index in the class table.
- int16_t num_type_arguments_; // Number of type arguments in flatten vector.
+ int16_t num_type_arguments_; // Number of type arguments in flattened vector.
int16_t num_own_type_arguments_; // Number of non-overlapping type arguments.
uint16_t num_native_fields_; // Number of native fields in class.
uint16_t state_bits_;
@@ -1904,10 +1904,14 @@
RawFunction* two_byte_function_;
RawFunction* external_one_byte_function_;
RawFunction* external_two_byte_function_;
+ RawTypedData* one_byte_bytecode_;
+ RawTypedData* two_byte_bytecode_;
RawObject** to() {
- return reinterpret_cast<RawObject**>(&ptr()->external_two_byte_function_);
+ return reinterpret_cast<RawObject**>(&ptr()->two_byte_bytecode_);
}
+ intptr_t num_registers_;
+
// A bitfield with two fields:
// type: Uninitialized, simple or complex.
// flags: Represents global/local, case insensitive, multiline.
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 092ddcd..057f8e9 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -2980,6 +2980,8 @@
reader->ReadAsSmi());
*reader->StringHandle() ^= reader->ReadObjectImpl();
regex.set_pattern(*reader->StringHandle());
+ regex.StoreNonPointer(®ex.raw_ptr()->num_registers_,
+ reader->Read<int32_t>());
regex.StoreNonPointer(®ex.raw_ptr()->type_flags_,
reader->Read<int8_t>());
@@ -3004,6 +3006,7 @@
// Write out all the other fields.
writer->Write<RawObject*>(ptr()->num_bracket_expressions_);
writer->WriteObjectImpl(ptr()->pattern_);
+ writer->Write<int32_t>(ptr()->num_registers_);
writer->Write<int8_t>(ptr()->type_flags_);
}
diff --git a/runtime/vm/regexp.cc b/runtime/vm/regexp.cc
index fcfa02c..db115a5 100644
--- a/runtime/vm/regexp.cc
+++ b/runtime/vm/regexp.cc
@@ -6,6 +6,8 @@
#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/unibrow-inl.h"
#include "vm/unicode.h"
@@ -17,6 +19,8 @@
namespace dart {
DECLARE_FLAG(bool, trace_irregexp);
+DEFINE_FLAG(bool, interpret_irregexp, false,
+ "Use irregexp bytecode interpreter");
// Default to generating optimized regexp code.
static const bool kRegexpOptimization = true;
@@ -294,16 +298,23 @@
public:
RegExpCompiler(intptr_t capture_count,
bool ignore_case,
- intptr_t specialization_cid);
+ bool is_one_byte);
intptr_t AllocateRegister() {
return next_register_++;
}
- RegExpEngine::CompilationResult Assemble(IRRegExpMacroAssembler* assembler,
- RegExpNode* start,
- intptr_t capture_count,
- const String& pattern);
+ RegExpEngine::CompilationResult Assemble(
+ IRRegExpMacroAssembler* assembler,
+ RegExpNode* start,
+ intptr_t capture_count,
+ const String& pattern);
+
+ RegExpEngine::CompilationResult Assemble(
+ BytecodeRegExpMacroAssembler* assembler,
+ RegExpNode* start,
+ intptr_t capture_count,
+ const String& pattern);
inline void AddWork(RegExpNode* node) { work_list_->Add(node); }
@@ -311,7 +322,7 @@
static const intptr_t kNumberOfRegistersOffset = 0;
static const intptr_t kCodeOffset = 1;
- IRRegExpMacroAssembler* macro_assembler() { return macro_assembler_; }
+ RegExpMacroAssembler* macro_assembler() { return macro_assembler_; }
EndNode* accept() { return accept_; }
static const intptr_t kMaxRecursion = 100;
@@ -322,11 +333,7 @@
void SetRegExpTooBig() { reg_exp_too_big_ = true; }
inline bool ignore_case() { return ignore_case_; }
- inline bool one_byte() const {
- return (specialization_cid_ == kOneByteStringCid ||
- specialization_cid_ == kExternalOneByteStringCid);
- }
- inline intptr_t specialization_cid() { return specialization_cid_; }
+ inline bool one_byte() const { return is_one_byte_; }
FrequencyCollator* frequency_collator() { return &frequency_collator_; }
intptr_t current_expansion_factor() { return current_expansion_factor_; }
@@ -343,9 +350,9 @@
intptr_t next_register_;
ZoneGrowableArray<RegExpNode*>* work_list_;
intptr_t recursion_depth_;
- IRRegExpMacroAssembler* macro_assembler_;
+ RegExpMacroAssembler* macro_assembler_;
bool ignore_case_;
- intptr_t specialization_cid_;
+ bool is_one_byte_;
bool reg_exp_too_big_;
intptr_t current_expansion_factor_;
FrequencyCollator frequency_collator_;
@@ -371,13 +378,14 @@
// Attempts to compile the regexp using an Irregexp code generator. Returns
// a fixed array or a null handle depending on whether it succeeded.
-RegExpCompiler::RegExpCompiler(intptr_t capture_count, bool ignore_case,
- intptr_t specialization_cid)
+RegExpCompiler::RegExpCompiler(intptr_t capture_count,
+ bool ignore_case,
+ bool is_one_byte)
: next_register_(2 * (capture_count + 1)),
work_list_(NULL),
recursion_depth_(0),
ignore_case_(ignore_case),
- specialization_cid_(specialization_cid),
+ is_one_byte_(is_one_byte),
reg_exp_too_big_(false),
current_expansion_factor_(1),
zone_(Thread::Current()->zone()) {
@@ -390,9 +398,7 @@
RegExpNode* start,
intptr_t capture_count,
const String& pattern) {
- static const bool use_slow_safe_regexp_compiler = false;
-
- macro_assembler->set_slow_safe(use_slow_safe_regexp_compiler);
+ macro_assembler->set_slow_safe(false /* use_slow_safe_regexp_compiler */);
macro_assembler_ = macro_assembler;
ZoneGrowableArray<RegExpNode*> work_list(0);
@@ -414,7 +420,34 @@
return RegExpEngine::CompilationResult(macro_assembler->backtrack_goto(),
macro_assembler->graph_entry(),
macro_assembler->num_blocks(),
- macro_assembler->num_stack_locals());
+ macro_assembler->num_stack_locals(),
+ next_register_);
+}
+
+
+RegExpEngine::CompilationResult RegExpCompiler::Assemble(
+ BytecodeRegExpMacroAssembler* macro_assembler,
+ RegExpNode* start,
+ intptr_t capture_count,
+ const String& pattern) {
+ macro_assembler->set_slow_safe(false /* use_slow_safe_regexp_compiler */);
+ macro_assembler_ = macro_assembler;
+
+ ZoneGrowableArray<RegExpNode*> work_list(0);
+ work_list_ = &work_list;
+ BlockLabel fail;
+ macro_assembler_->PushBacktrack(&fail);
+ Trace new_trace;
+ start->Emit(this, &new_trace);
+ macro_assembler_->BindBlock(&fail);
+ macro_assembler_->Fail();
+ while (!work_list.is_empty()) {
+ work_list.RemoveLast()->Emit(this, &new_trace);
+ }
+ if (reg_exp_too_big_) return IrregexpRegExpTooBig();
+
+ TypedData& bytecode = TypedData::ZoneHandle(macro_assembler->GetBytecode());
+ return RegExpEngine::CompilationResult(&bytecode, next_register_);
}
@@ -4976,10 +5009,11 @@
}
-RegExpEngine::CompilationResult RegExpEngine::Compile(
+RegExpEngine::CompilationResult RegExpEngine::CompileIR(
RegExpCompileData* data,
const ParsedFunction* parsed_function,
const ZoneGrowableArray<const ICData*>& ic_data_array) {
+ ASSERT(!FLAG_interpret_irregexp);
Zone* zone = Thread::Current()->zone();
const Function& function = parsed_function->function();
@@ -4995,7 +5029,7 @@
const bool ignore_case = regexp.is_ignore_case();
const bool is_global = regexp.is_global();
- RegExpCompiler compiler(data->capture_count, ignore_case, specialization_cid);
+ RegExpCompiler compiler(data->capture_count, ignore_case, is_one_byte);
// TODO(zerny): Frequency sampling is currently disabled because of several
// issues. We do not want to store subject strings in the regexp object since
@@ -5098,6 +5132,120 @@
}
+RegExpEngine::CompilationResult RegExpEngine::CompileBytecode(
+ RegExpCompileData* data,
+ const JSRegExp& regexp,
+ bool is_one_byte,
+ Zone* zone) {
+ ASSERT(FLAG_interpret_irregexp);
+ const String& pattern = String::Handle(zone, regexp.pattern());
+
+ ASSERT(!regexp.IsNull());
+ ASSERT(!pattern.IsNull());
+
+ const bool ignore_case = regexp.is_ignore_case();
+ const bool is_global = regexp.is_global();
+
+ RegExpCompiler compiler(data->capture_count, ignore_case, is_one_byte);
+
+ // TODO(zerny): Frequency sampling is currently disabled because of several
+ // issues. We do not want to store subject strings in the regexp object since
+ // they might be long and we should not prevent their garbage collection.
+ // Passing them to this function explicitly does not help, since we must
+ // generate exactly the same IR for both the unoptimizing and optimizing
+ // pipelines (otherwise it gets confused when i.e. deopt id's differ).
+ // An option would be to store sampling results in the regexp object, but
+ // I'm not sure the performance gains are relevant enough.
+
+ // Wrap the body of the regexp in capture #0.
+ RegExpNode* captured_body = RegExpCapture::ToNode(data->tree,
+ 0,
+ &compiler,
+ compiler.accept());
+
+ RegExpNode* node = captured_body;
+ bool is_end_anchored = data->tree->IsAnchoredAtEnd();
+ bool is_start_anchored = data->tree->IsAnchoredAtStart();
+ intptr_t max_length = data->tree->max_match();
+ if (!is_start_anchored) {
+ // Add a .*? at the beginning, outside the body capture, unless
+ // this expression is anchored at the beginning.
+ RegExpNode* loop_node =
+ RegExpQuantifier::ToNode(0,
+ RegExpTree::kInfinity,
+ false,
+ new(zone) RegExpCharacterClass('*'),
+ &compiler,
+ captured_body,
+ data->contains_anchor);
+
+ if (data->contains_anchor) {
+ // Unroll loop once, to take care of the case that might start
+ // at the start of input.
+ ChoiceNode* first_step_node = new(zone) ChoiceNode(2, zone);
+ first_step_node->AddAlternative(GuardedAlternative(captured_body));
+ first_step_node->AddAlternative(GuardedAlternative(
+ new(zone) TextNode(
+ new(zone) RegExpCharacterClass('*'), loop_node)));
+ node = first_step_node;
+ } else {
+ node = loop_node;
+ }
+ }
+ if (is_one_byte) {
+ node = node->FilterOneByte(RegExpCompiler::kMaxRecursion, ignore_case);
+ // Do it again to propagate the new nodes to places where they were not
+ // put because they had not been calculated yet.
+ if (node != NULL) {
+ node = node->FilterOneByte(RegExpCompiler::kMaxRecursion, ignore_case);
+ }
+ }
+
+ if (node == NULL) node = new(zone) EndNode(EndNode::BACKTRACK, zone);
+ data->node = node;
+ Analysis analysis(ignore_case, is_one_byte);
+ analysis.EnsureAnalyzed(node);
+ if (analysis.has_failed()) {
+ const char* error_message = analysis.error_message();
+ return CompilationResult(error_message);
+ }
+
+ // Bytecode regexp implementation.
+
+ ZoneGrowableArray<uint8_t> buffer(zone, 1024);
+ BytecodeRegExpMacroAssembler* macro_assembler =
+ new(zone) BytecodeRegExpMacroAssembler(&buffer, zone);
+
+ // Inserted here, instead of in Assembler, because it depends on information
+ // in the AST that isn't replicated in the Node structure.
+ static const intptr_t kMaxBacksearchLimit = 1024;
+ if (is_end_anchored &&
+ !is_start_anchored &&
+ max_length < kMaxBacksearchLimit) {
+ macro_assembler->SetCurrentPositionFromEnd(max_length);
+ }
+
+ if (is_global) {
+ macro_assembler->set_global_mode(
+ (data->tree->min_match() > 0)
+ ? RegExpMacroAssembler::GLOBAL_NO_ZERO_LENGTH_CHECK
+ : RegExpMacroAssembler::GLOBAL);
+ }
+
+ RegExpEngine::CompilationResult result =
+ compiler.Assemble(macro_assembler,
+ node,
+ data->capture_count,
+ pattern);
+
+ if (FLAG_trace_irregexp) {
+ macro_assembler->PrintBlocks();
+ }
+
+ return result;
+}
+
+
static void CreateSpecializedFunction(Zone* zone,
const JSRegExp& regexp,
intptr_t specialization_cid,
diff --git a/runtime/vm/regexp.h b/runtime/vm/regexp.h
index e0808d6..394279d 100644
--- a/runtime/vm/regexp.h
+++ b/runtime/vm/regexp.h
@@ -1377,16 +1377,30 @@
graph_entry(NULL),
num_blocks(-1),
num_stack_locals(-1),
- error_message(error_message) {}
+ error_message(error_message),
+ bytecode(NULL),
+ num_registers(-1) {}
+
+ CompilationResult(TypedData* bytecode, intptr_t num_registers)
+ : backtrack_goto(NULL),
+ graph_entry(NULL),
+ num_blocks(-1),
+ num_stack_locals(-1),
+ error_message(NULL),
+ bytecode(bytecode),
+ num_registers(num_registers) {}
+
CompilationResult(IndirectGotoInstr* backtrack_goto,
GraphEntryInstr* graph_entry,
intptr_t num_blocks,
- intptr_t num_stack_locals)
+ intptr_t num_stack_locals,
+ intptr_t num_registers)
: backtrack_goto(backtrack_goto),
graph_entry(graph_entry),
num_blocks(num_blocks),
num_stack_locals(num_stack_locals),
- error_message(NULL) {}
+ error_message(NULL),
+ bytecode(NULL) {}
IndirectGotoInstr* backtrack_goto;
GraphEntryInstr* graph_entry;
@@ -1394,13 +1408,22 @@
const intptr_t num_stack_locals;
const char* error_message;
+
+ TypedData* bytecode;
+ intptr_t num_registers;
};
- static CompilationResult Compile(
+ static CompilationResult CompileIR(
RegExpCompileData* input,
const ParsedFunction* parsed_function,
const ZoneGrowableArray<const ICData*>& ic_data_array);
+ static CompilationResult CompileBytecode(
+ RegExpCompileData* data,
+ const JSRegExp& regexp,
+ bool is_one_byte,
+ Zone* zone);
+
static RawJSRegExp* CreateJSRegExp(
Zone* zone,
const String& pattern,
diff --git a/runtime/vm/regexp_assembler.cc b/runtime/vm/regexp_assembler.cc
index e4e3247..9e01f35 100644
--- a/runtime/vm/regexp_assembler.cc
+++ b/runtime/vm/regexp_assembler.cc
@@ -4,70 +4,10 @@
#include "vm/regexp_assembler.h"
-#include "vm/bit_vector.h"
-#include "vm/compiler.h"
-#include "vm/dart_entry.h"
-#include "vm/flow_graph_builder.h"
-#include "vm/il_printer.h"
-#include "vm/object_store.h"
#include "vm/regexp.h"
-#include "vm/resolver.h"
-#include "vm/stack_frame.h"
-#include "vm/unibrow-inl.h"
-#include "vm/unicode.h"
-
-#define Z zone()
-
-// Debugging output macros. TAG() is called at the head of each interesting
-// function and prints its name during execution if irregexp tracing is enabled.
-#define TAG() if (FLAG_trace_irregexp) { TAG_(); }
-#define TAG_() \
- Print(PushArgument( \
- Bind(new(Z) ConstantInstr(String::ZoneHandle(Z, String::Concat( \
- String::Handle(String::New("TAG: ")), \
- String::Handle(String::New(__FUNCTION__)), Heap::kOld))))));
-
-#define PRINT(arg) if (FLAG_trace_irregexp) { Print(arg); }
namespace dart {
-DEFINE_FLAG(bool, trace_irregexp, false, "Trace irregexps");
-
-
-static const intptr_t kInvalidTryIndex = CatchClauseNode::kInvalidTryIndex;
-static const intptr_t kNoSourcePos = Scanner::kNoSourcePos;
-static const intptr_t kMinStackSize = 512;
-
-
-void PrintUtf16(uint16_t c) {
- const char* format = (0x20 <= c && c <= 0x7F) ?
- "%c" : (c <= 0xff) ? "\\x%02x" : "\\u%04x";
- OS::Print(format, c);
-}
-
-
-/*
- * This assembler uses the following main local variables:
- * - stack_: A pointer to a growable list which we use as an all-purpose stack
- * storing backtracking offsets, positions & stored register values.
- * - current_character_: Stores the currently loaded characters (possibly more
- * than one).
- * - current_position_: The current position within the string, stored as a
- * negative offset from the end of the string (i.e. the
- * position corresponding to str[0] is -str.length).
- * Note that current_position_ is *not* byte-based, unlike
- * original V8 code.
- *
- * Results are returned though an array of capture indices, stored at
- * matches_param_. A null array specifies a failure to match. The match indices
- * [start_inclusive, end_exclusive] for capture group i are stored at positions
- * matches_param_[i * 2] and matches_param_[i * 2 + 1], respectively. Match
- * indices of -1 denote non-matched groups. Note that we store these indices
- * as a negative offset from the end of the string in registers_array_
- * during processing, and convert them to standard indexes when copying them
- * to matches_param_ on successful match.
- */
-
RegExpMacroAssembler::RegExpMacroAssembler(Zone* zone)
: slow_safe_compiler_(false),
global_mode_(NOT_GLOBAL),
@@ -78,1858 +18,4 @@
RegExpMacroAssembler::~RegExpMacroAssembler() {
}
-
-IRRegExpMacroAssembler::IRRegExpMacroAssembler(
- intptr_t specialization_cid,
- intptr_t capture_count,
- const ParsedFunction* parsed_function,
- const ZoneGrowableArray<const ICData*>& ic_data_array,
- Zone* zone)
- : RegExpMacroAssembler(zone),
- specialization_cid_(specialization_cid),
- parsed_function_(parsed_function),
- ic_data_array_(ic_data_array),
- current_instruction_(NULL),
- stack_(NULL),
- stack_pointer_(NULL),
- current_character_(NULL),
- current_position_(NULL),
- string_param_(NULL),
- string_param_length_(NULL),
- start_index_param_(NULL),
- registers_count_(0),
- saved_registers_count_((capture_count + 1) * 2),
- stack_array_cell_(Array::ZoneHandle(zone, Array::New(1, Heap::kOld))),
- // The registers array is allocated at a fixed size after assembly.
- registers_array_(TypedData::ZoneHandle(zone, TypedData::null())) {
- switch (specialization_cid) {
- case kOneByteStringCid:
- case kExternalOneByteStringCid: mode_ = ASCII; break;
- case kTwoByteStringCid:
- case kExternalTwoByteStringCid: mode_ = UC16; break;
- default: UNREACHABLE();
- }
-
- InitializeLocals();
-
- // Allocate an initial stack backing of the minimum stack size. The stack
- // backing is indirectly referred to so we can reuse it on subsequent matches
- // even in the case where the backing has been enlarged and thus reallocated.
- stack_array_cell_.SetAt(0, TypedData::Handle(zone,
- TypedData::New(kTypedDataInt32ArrayCid, kMinStackSize / 4, Heap::kOld)));
-
- // Create and generate all preset blocks.
- entry_block_ =
- new(zone) GraphEntryInstr(
- *parsed_function_,
- new(zone) TargetEntryInstr(block_id_.Alloc(), kInvalidTryIndex),
- Isolate::kNoDeoptId);
- start_block_ =
- new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
- success_block_ =
- new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
- backtrack_block_ =
- new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
- exit_block_ =
- new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
-
- GenerateEntryBlock();
- GenerateSuccessBlock();
- GenerateExitBlock();
-
- blocks_.Add(entry_block_);
- blocks_.Add(entry_block_->normal_entry());
- blocks_.Add(start_block_);
- blocks_.Add(success_block_);
- blocks_.Add(backtrack_block_);
- blocks_.Add(exit_block_);
-
- // Begin emission at the start_block_.
- set_current_instruction(start_block_);
-}
-
-
-IRRegExpMacroAssembler::~IRRegExpMacroAssembler() { }
-
-
-void IRRegExpMacroAssembler::InitializeLocals() {
- // All generated functions are expected to have a current-context variable.
- // This variable is unused in irregexp functions.
- parsed_function_->current_context_var()->set_index(GetNextLocalIndex());
-
- // Create local variables and parameters.
- stack_ = Local(Symbols::stack());
- stack_pointer_ = Local(Symbols::stack_pointer());
- registers_ = Local(Symbols::position_registers());
- current_character_ = Local(Symbols::current_character());
- current_position_ = Local(Symbols::current_position());
- string_param_length_ = Local(Symbols::string_param_length());
- capture_length_ = Local(Symbols::capture_length());
- match_start_index_ = Local(Symbols::match_start_index());
- capture_start_index_ = Local(Symbols::capture_start_index());
- match_end_index_ = Local(Symbols::match_end_index());
- char_in_capture_ = Local(Symbols::char_in_capture());
- char_in_match_ = Local(Symbols::char_in_match());
- index_temp_ = Local(Symbols::index_temp());
- result_ = Local(Symbols::result());
-
- string_param_ = Parameter(Symbols::string_param(), 0);
- start_index_param_ = Parameter(Symbols::start_index_param(), 1);
-}
-
-
-void IRRegExpMacroAssembler::GenerateEntryBlock() {
- set_current_instruction(entry_block_->normal_entry());
- TAG();
-
- // Store string.length.
- PushArgumentInstr* string_push = PushLocal(string_param_);
-
- StoreLocal(
- string_param_length_,
- Bind(InstanceCall(
- InstanceCallDescriptor(
- String::ZoneHandle(Field::GetterSymbol(Symbols::Length()))),
- string_push)));
-
- // Store (start_index - string.length) as the current position (since it's a
- // negative offset from the end of the string).
- PushArgumentInstr* start_index_push = PushLocal(start_index_param_);
- PushArgumentInstr* length_push = PushLocal(string_param_length_);
-
- StoreLocal(current_position_, Bind(Sub(start_index_push, length_push)));
-
- // Generate a local list variable to represent "registers" and
- // initialize capture registers (others remain garbage).
- StoreLocal(registers_, Bind(new(Z) ConstantInstr(registers_array_)));
- ClearRegisters(0, saved_registers_count_ - 1);
-
- // Generate a local list variable to represent the backtracking stack.
- PushArgumentInstr* stack_cell_push =
- PushArgument(Bind(new(Z) ConstantInstr(stack_array_cell_)));
- StoreLocal(stack_, Bind(InstanceCall(
- InstanceCallDescriptor::FromToken(Token::kINDEX),
- stack_cell_push,
- PushArgument(Bind(Uint64Constant(0))))));
- StoreLocal(stack_pointer_, Bind(Int64Constant(-1)));
-
- // Jump to the start block.
- current_instruction_->Goto(start_block_);
-}
-
-
-void IRRegExpMacroAssembler::GenerateBacktrackBlock() {
- set_current_instruction(backtrack_block_);
- TAG();
- CheckPreemption();
-
- const intptr_t entries_count = entry_block_->indirect_entries().length();
-
- TypedData& offsets = TypedData::ZoneHandle(Z,
- TypedData::New(kTypedDataInt32ArrayCid, entries_count, Heap::kOld));
-
- PushArgumentInstr* block_offsets_push =
- PushArgument(Bind(new(Z) ConstantInstr(offsets)));
- PushArgumentInstr* block_id_push = PushArgument(Bind(PopStack()));
-
- Value* offset_value =
- Bind(InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
- block_offsets_push,
- block_id_push));
-
- backtrack_goto_ = new(Z) IndirectGotoInstr(&offsets, offset_value);
- CloseBlockWith(backtrack_goto_);
-
- // Add an edge from the "indirect" goto to each of the targets.
- for (intptr_t j = 0; j < entries_count; j++) {
- backtrack_goto_->AddSuccessor(
- TargetWithJoinGoto(entry_block_->indirect_entries().At(j)));
- }
-}
-
-
-void IRRegExpMacroAssembler::GenerateSuccessBlock() {
- set_current_instruction(success_block_);
- TAG();
-
- Value* type = Bind(new(Z) ConstantInstr(
- TypeArguments::ZoneHandle(Z, TypeArguments::null())));
- Value* length = Bind(Uint64Constant(saved_registers_count_));
- Value* array = Bind(new(Z) CreateArrayInstr(kNoSourcePos, type, length));
- StoreLocal(result_, array);
-
- // Store captured offsets in the `matches` parameter.
- for (intptr_t i = 0; i < saved_registers_count_; i++) {
- PushArgumentInstr* matches_push = PushLocal(result_);
- PushArgumentInstr* index_push = PushArgument(Bind(Uint64Constant(i)));
-
- // Convert negative offsets from the end of the string to string indices.
- // TODO(zerny): use positive offsets from the get-go.
- PushArgumentInstr* offset_push = PushArgument(LoadRegister(i));
- PushArgumentInstr* len_push = PushLocal(string_param_length_);
- PushArgumentInstr* value_push =
- PushArgument(Bind(Add(offset_push, len_push)));
-
- Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
- matches_push,
- index_push,
- value_push));
- }
-
- // Print the result if tracing.
- PRINT(PushLocal(result_));
-
- // Return true on success.
- AppendInstruction(new(Z) ReturnInstr(kNoSourcePos, Bind(LoadLocal(result_))));
-}
-
-
-void IRRegExpMacroAssembler::GenerateExitBlock() {
- set_current_instruction(exit_block_);
- TAG();
-
- // Return false on failure.
- AppendInstruction(new(Z) ReturnInstr(kNoSourcePos, Bind(LoadLocal(result_))));
-}
-
-
-void IRRegExpMacroAssembler::FinalizeRegistersArray() {
- ASSERT(registers_count_ >= saved_registers_count_);
- registers_array_ =
- TypedData::New(kTypedDataInt32ArrayCid, registers_count_, Heap::kOld);
-}
-
-
-#if defined(TARGET_ARCH_ARM64) || \
- defined(TARGET_ARCH_ARM) || \
- defined(TARGET_ARCH_MIPS)
-// Disabling unaligned accesses forces the regexp engine to load characters one
-// by one instead of up to 4 at once, along with the associated performance hit.
-// TODO(zerny): Be less conservative about disabling unaligned accesses.
-// For instance, ARMv6 supports unaligned accesses. Once it is enabled here,
-// update LoadCodeUnitsInstr methods for the appropriate architectures.
-static const bool kEnableUnalignedAccesses = false;
-#else
-static const bool kEnableUnalignedAccesses = true;
-#endif
-bool IRRegExpMacroAssembler::CanReadUnaligned() {
- return kEnableUnalignedAccesses && !slow_safe();
-}
-
-
-RawArray* IRRegExpMacroAssembler::Execute(
- const Function& function,
- const String& input,
- const Smi& start_offset,
- Zone* zone) {
- // Create the argument list.
- const Array& args = Array::Handle(Array::New(2));
- args.SetAt(0, input);
- args.SetAt(1, start_offset);
-
- // And finally call the generated code.
-
- const Object& retval =
- Object::Handle(zone, DartEntry::InvokeFunction(function, args));
- if (retval.IsError()) {
- const Error& error = Error::Cast(retval);
- OS::Print("%s\n", error.ToErrorCString());
- // Should never happen.
- UNREACHABLE();
- }
-
- if (retval.IsNull()) {
- return Array::null();
- }
-
- ASSERT(retval.IsArray());
- return Array::Cast(retval).raw();
-}
-
-
-RawBool* IRRegExpMacroAssembler::CaseInsensitiveCompareUC16(
- RawString* str_raw,
- RawSmi* lhs_index_raw,
- RawSmi* rhs_index_raw,
- RawSmi* length_raw) {
- const String& str = String::Handle(str_raw);
- const Smi& lhs_index = Smi::Handle(lhs_index_raw);
- const Smi& rhs_index = Smi::Handle(rhs_index_raw);
- const Smi& length = Smi::Handle(length_raw);
-
- // TODO(zerny): Optimize as single instance. V8 has this as an
- // isolate member.
- unibrow::Mapping<unibrow::Ecma262Canonicalize> canonicalize;
-
- for (intptr_t i = 0; i < length.Value(); i++) {
- int32_t c1 = str.CharAt(lhs_index.Value() + i);
- int32_t c2 = str.CharAt(rhs_index.Value() + i);
- if (c1 != c2) {
- int32_t s1[1] = { c1 };
- canonicalize.get(c1, '\0', s1);
- if (s1[0] != c2) {
- int32_t s2[1] = { c2 };
- canonicalize.get(c2, '\0', s2);
- if (s1[0] != s2[0]) {
- return Bool::False().raw();
- }
- }
- }
- }
- return Bool::True().raw();
-}
-
-
-LocalVariable* IRRegExpMacroAssembler::Parameter(const String& name,
- intptr_t index) const {
- const Type& local_type = Type::ZoneHandle(Z, Type::DynamicType());
- LocalVariable* local =
- new(Z) LocalVariable(kNoSourcePos, name, local_type);
-
- intptr_t param_frame_index = kParamEndSlotFromFp + kParamCount - index;
- local->set_index(param_frame_index);
-
- return local;
-}
-
-
-LocalVariable* IRRegExpMacroAssembler::Local(const String& name) {
- const Type& local_type = Type::ZoneHandle(Z, Type::DynamicType());
- LocalVariable* local =
- new(Z) LocalVariable(kNoSourcePos, name, local_type);
- local->set_index(GetNextLocalIndex());
-
- return local;
-}
-
-
-ConstantInstr* IRRegExpMacroAssembler::Int64Constant(int64_t value) const {
- return new(Z) ConstantInstr(
- Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld)));
-}
-
-
-ConstantInstr* IRRegExpMacroAssembler::Uint64Constant(uint64_t value) const {
- return new(Z) ConstantInstr(
- Integer::ZoneHandle(Z, Integer::NewFromUint64(value, Heap::kOld)));
-}
-
-
-ConstantInstr* IRRegExpMacroAssembler::BoolConstant(bool value) const {
- return new(Z) ConstantInstr(value ? Bool::True() : Bool::False());
-}
-
-
-ConstantInstr* IRRegExpMacroAssembler::StringConstant(const char* value) const {
- return new(Z) ConstantInstr(
- String::ZoneHandle(Z, String::New(value, Heap::kOld)));
-}
-
-
-ConstantInstr* IRRegExpMacroAssembler::WordCharacterMapConstant() const {
- const Library& lib = Library::Handle(Z, Library::CoreLibrary());
- const Class& regexp_class = Class::Handle(Z,
- lib.LookupClassAllowPrivate(Symbols::JSSyntaxRegExp()));
- const Field& word_character_field = Field::ZoneHandle(Z,
- regexp_class.LookupStaticField(Symbols::_wordCharacterMap()));
- ASSERT(!word_character_field.IsNull());
-
- if (word_character_field.IsUninitialized()) {
- word_character_field.EvaluateInitializer();
- }
- ASSERT(!word_character_field.IsUninitialized());
-
- return new(Z) ConstantInstr(
- Instance::ZoneHandle(Z, word_character_field.value()));
-}
-
-
-ComparisonInstr* IRRegExpMacroAssembler::Comparison(
- ComparisonKind kind, PushArgumentInstr* lhs, PushArgumentInstr* rhs) {
- Token::Kind strict_comparison = Token::kEQ_STRICT;
- Token::Kind intermediate_operator = Token::kILLEGAL;
- switch (kind) {
- case kEQ:
- intermediate_operator = Token::kEQ;
- break;
- case kNE:
- intermediate_operator = Token::kEQ;
- strict_comparison = Token::kNE_STRICT;
- break;
- case kLT:
- intermediate_operator = Token::kLT;
- break;
- case kGT:
- intermediate_operator = Token::kGT;
- break;
- case kLTE:
- intermediate_operator = Token::kLTE;
- break;
- case kGTE:
- intermediate_operator = Token::kGTE;
- break;
- default:
- UNREACHABLE();
- }
-
- ASSERT(intermediate_operator != Token::kILLEGAL);
-
- Value* lhs_value =
- Bind(InstanceCall(
- InstanceCallDescriptor::FromToken(intermediate_operator),
- lhs,
- rhs));
- Value* rhs_value = Bind(BoolConstant(true));
-
- return new(Z) StrictCompareInstr(
- kNoSourcePos, strict_comparison, lhs_value, rhs_value, true);
-}
-
-ComparisonInstr* IRRegExpMacroAssembler::Comparison(
- ComparisonKind kind, Definition* lhs, Definition* rhs) {
- PushArgumentInstr* lhs_push = PushArgument(Bind(lhs));
- PushArgumentInstr* rhs_push = PushArgument(Bind(rhs));
- return Comparison(kind, lhs_push, rhs_push);
-}
-
-
-StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
- const Function& function) const {
- ZoneGrowableArray<PushArgumentInstr*>* arguments =
- new(Z) ZoneGrowableArray<PushArgumentInstr*>(0);
- return StaticCall(function, arguments);
-}
-
-
-StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
- const Function& function,
- PushArgumentInstr* arg1) const {
- ZoneGrowableArray<PushArgumentInstr*>* arguments =
- new(Z) ZoneGrowableArray<PushArgumentInstr*>(1);
- arguments->Add(arg1);
-
- return StaticCall(function, arguments);
-}
-
-
-StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
- const Function& function,
- PushArgumentInstr* arg1,
- PushArgumentInstr* arg2) const {
- ZoneGrowableArray<PushArgumentInstr*>* arguments =
- new(Z) ZoneGrowableArray<PushArgumentInstr*>(2);
- arguments->Add(arg1);
- arguments->Add(arg2);
-
- return StaticCall(function, arguments);
-}
-
-
-StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
- const Function& function,
- ZoneGrowableArray<PushArgumentInstr*>* arguments) const {
- return new(Z) StaticCallInstr(kNoSourcePos,
- function,
- Object::null_array(),
- arguments,
- ic_data_array_);
-}
-
-
-InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
- const InstanceCallDescriptor& desc,
- PushArgumentInstr* arg1) const {
- ZoneGrowableArray<PushArgumentInstr*>* arguments =
- new(Z) ZoneGrowableArray<PushArgumentInstr*>(1);
- arguments->Add(arg1);
-
- return InstanceCall(desc, arguments);
-}
-
-
-InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
- const InstanceCallDescriptor& desc,
- PushArgumentInstr* arg1,
- PushArgumentInstr* arg2) const {
- ZoneGrowableArray<PushArgumentInstr*>* arguments =
- new(Z) ZoneGrowableArray<PushArgumentInstr*>(2);
- arguments->Add(arg1);
- arguments->Add(arg2);
-
- return InstanceCall(desc, arguments);
-}
-
-
-InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
- const InstanceCallDescriptor& desc,
- PushArgumentInstr* arg1,
- PushArgumentInstr* arg2,
- PushArgumentInstr* arg3) const {
- ZoneGrowableArray<PushArgumentInstr*>* arguments =
- new(Z) ZoneGrowableArray<PushArgumentInstr*>(3);
- arguments->Add(arg1);
- arguments->Add(arg2);
- arguments->Add(arg3);
-
- return InstanceCall(desc, arguments);
-}
-
-
-InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
- const InstanceCallDescriptor& desc,
- ZoneGrowableArray<PushArgumentInstr*> *arguments) const {
- return
- new(Z) InstanceCallInstr(kNoSourcePos,
- desc.name,
- desc.token_kind,
- arguments,
- Object::null_array(),
- desc.checked_argument_count,
- ic_data_array_);
-}
-
-
-LoadLocalInstr* IRRegExpMacroAssembler::LoadLocal(LocalVariable* local) const {
- return new(Z) LoadLocalInstr(*local);
-}
-
-
-void IRRegExpMacroAssembler::StoreLocal(LocalVariable* local,
- Value* value) {
- Do(new(Z) StoreLocalInstr(*local, value));
-}
-
-
-void IRRegExpMacroAssembler::set_current_instruction(Instruction* instruction) {
- current_instruction_ = instruction;
-}
-
-
-Value* IRRegExpMacroAssembler::Bind(Definition* definition) {
- AppendInstruction(definition);
- definition->set_temp_index(temp_id_.Alloc());
-
- return new(Z) Value(definition);
-}
-
-
-void IRRegExpMacroAssembler::Do(Definition* definition) {
- AppendInstruction(definition);
-}
-
-
-Value* IRRegExpMacroAssembler::BindLoadLocal(const LocalVariable& local) {
- if (local.IsConst()) {
- return Bind(new(Z) ConstantInstr(*local.ConstValue()));
- }
- ASSERT(!local.is_captured());
- return Bind(new(Z) LoadLocalInstr(local));
-}
-
-
-// In some cases, the V8 irregexp engine generates unreachable code by emitting
-// a jmp not followed by a bind. We cannot do the same, since it is impossible
-// to append to a block following a jmp. In such cases, assume that we are doing
-// the correct thing, but output a warning when tracing.
-#define HANDLE_DEAD_CODE_EMISSION() \
- if (current_instruction_ == NULL) { \
- if (FLAG_trace_irregexp) { \
- OS::Print("WARNING: Attempting to append to a closed assembler. " \
- "This could be either a bug or generation of dead code " \
- "inherited from V8.\n"); \
- } \
- BlockLabel dummy; \
- BindBlock(&dummy); \
- }
-
-void IRRegExpMacroAssembler::AppendInstruction(Instruction* instruction) {
- HANDLE_DEAD_CODE_EMISSION();
-
- ASSERT(current_instruction_ != NULL);
- ASSERT(current_instruction_->next() == NULL);
-
- temp_id_.Dealloc(instruction->InputCount());
- arg_id_.Dealloc(instruction->ArgumentCount());
-
- current_instruction_->LinkTo(instruction);
- set_current_instruction(instruction);
-}
-
-
-void IRRegExpMacroAssembler::CloseBlockWith(Instruction* instruction) {
- HANDLE_DEAD_CODE_EMISSION();
-
- ASSERT(current_instruction_ != NULL);
- ASSERT(current_instruction_->next() == NULL);
-
- temp_id_.Dealloc(instruction->InputCount());
- arg_id_.Dealloc(instruction->ArgumentCount());
-
- current_instruction_->LinkTo(instruction);
- set_current_instruction(NULL);
-}
-
-
-void IRRegExpMacroAssembler::GoTo(BlockLabel* to) {
- if (to == NULL) {
- Backtrack();
- } else {
- to->SetLinked();
- GoTo(to->block());
- }
-}
-
-
-// Closes the current block with a goto, and unsets current_instruction_.
-// BindBlock() must be called before emission can continue.
-void IRRegExpMacroAssembler::GoTo(JoinEntryInstr* to) {
- HANDLE_DEAD_CODE_EMISSION();
-
- ASSERT(current_instruction_ != NULL);
- ASSERT(current_instruction_->next() == NULL);
- current_instruction_->Goto(to);
- set_current_instruction(NULL);
-}
-
-
-PushArgumentInstr* IRRegExpMacroAssembler::PushArgument(Value* value) {
- arg_id_.Alloc();
- PushArgumentInstr* push = new(Z) PushArgumentInstr(value);
- // Do *not* use Do() for push argument instructions.
- AppendInstruction(push);
- return push;
-}
-
-
-PushArgumentInstr* IRRegExpMacroAssembler::PushLocal(LocalVariable* local) {
- return PushArgument(Bind(LoadLocal(local)));
-}
-
-
-void IRRegExpMacroAssembler::Print(const char* str) {
- Print(PushArgument(
- Bind(new(Z) ConstantInstr(
- String::ZoneHandle(Z, String::New(str, Heap::kOld))))));
-}
-
-
-void IRRegExpMacroAssembler::Print(PushArgumentInstr* argument) {
- const Library& lib = Library::Handle(Library::CoreLibrary());
- const Function& print_fn = Function::ZoneHandle(
- Z, lib.LookupFunctionAllowPrivate(Symbols::print()));
- Do(StaticCall(print_fn, argument));
-}
-
-
-void IRRegExpMacroAssembler::PrintBlocks() {
- for (intptr_t i = 0; i < blocks_.length(); i++) {
- FlowGraphPrinter::PrintBlock(blocks_[i], false);
- }
-}
-
-
-intptr_t IRRegExpMacroAssembler::stack_limit_slack() {
- return 32;
-}
-
-
-void IRRegExpMacroAssembler::AdvanceCurrentPosition(intptr_t by) {
- TAG();
- if (by != 0) {
- PushArgumentInstr* cur_pos_push = PushLocal(current_position_);
- PushArgumentInstr* by_push = PushArgument(Bind(Int64Constant(by)));
-
- Value* new_pos_value = Bind(Add(cur_pos_push, by_push));
- StoreLocal(current_position_, new_pos_value);
- }
-}
-
-
-void IRRegExpMacroAssembler::AdvanceRegister(intptr_t reg, intptr_t by) {
- TAG();
- ASSERT(reg >= 0);
- ASSERT(reg < registers_count_);
-
- if (by != 0) {
- PushArgumentInstr* registers_push = PushLocal(registers_);
- PushArgumentInstr* index_push = PushRegisterIndex(reg);
- PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
- PushArgumentInstr* by_push = PushArgument(Bind(Int64Constant(by)));
- PushArgumentInstr* value_push = PushArgument(Bind(Add(reg_push, by_push)));
- StoreRegister(registers_push, index_push, value_push);
- }
-}
-
-
-void IRRegExpMacroAssembler::Backtrack() {
- TAG();
- GoTo(backtrack_block_);
-}
-
-
-// A BindBlock is analogous to assigning a label to a basic block.
-// 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->block()->next() == NULL);
-
- label->SetBound(block_id_.Alloc());
- blocks_.Add(label->block());
-
- if (current_instruction_ != NULL) {
- GoTo(label);
- }
- set_current_instruction(label->block());
-
- // Print the id of the current block if tracing.
- PRINT(PushArgument(Bind(Uint64Constant(label->block()->block_id()))));
-}
-
-
-intptr_t IRRegExpMacroAssembler::GetNextLocalIndex() {
- intptr_t id = local_id_.Alloc();
- return kFirstLocalSlotFromFp - id;
-}
-
-
-Value* IRRegExpMacroAssembler::LoadRegister(intptr_t index) {
- PushArgumentInstr* registers_push = PushLocal(registers_);
- PushArgumentInstr* index_push = PushRegisterIndex(index);
- return Bind(InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
- registers_push,
- index_push));
-}
-
-void IRRegExpMacroAssembler::StoreRegister(intptr_t index, intptr_t value) {
- PushArgumentInstr* registers_push = PushLocal(registers_);
- PushArgumentInstr* index_push = PushRegisterIndex(index);
- PushArgumentInstr* value_push = PushArgument(Bind(Uint64Constant(value)));
- StoreRegister(registers_push, index_push, value_push);
-}
-
-
-void IRRegExpMacroAssembler::StoreRegister(PushArgumentInstr* registers,
- PushArgumentInstr* index,
- PushArgumentInstr* value) {
- TAG();
- Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
- registers,
- index,
- value));
-}
-
-PushArgumentInstr* IRRegExpMacroAssembler::PushRegisterIndex(intptr_t index) {
- if (registers_count_ <= index) {
- registers_count_ = index + 1;
- }
- return PushArgument(Bind(Uint64Constant(index)));
-}
-
-
-void IRRegExpMacroAssembler::CheckCharacter(uint32_t c, BlockLabel* on_equal) {
- TAG();
- Definition* cur_char_def = LoadLocal(current_character_);
- Definition* char_def = Uint64Constant(c);
-
- BranchOrBacktrack(Comparison(kEQ, cur_char_def, char_def), on_equal);
-}
-
-
-void IRRegExpMacroAssembler::CheckCharacterGT(uint16_t limit,
- BlockLabel* on_greater) {
- TAG();
- BranchOrBacktrack(Comparison(kGT,
- LoadLocal(current_character_),
- Uint64Constant(limit)),
- on_greater);
-}
-
-
-void IRRegExpMacroAssembler::CheckAtStart(BlockLabel* on_at_start) {
- TAG();
-
- BlockLabel not_at_start;
-
- // Did we start the match at the start of the string at all?
- BranchOrBacktrack(Comparison(kNE,
- LoadLocal(start_index_param_),
- Uint64Constant(0)),
- ¬_at_start);
-
- // If we did, are we still at the start of the input, i.e. is
- // (offset == string_length * -1)?
- Definition* neg_len_def =
- InstanceCall(InstanceCallDescriptor::FromToken(Token::kNEGATE),
- PushLocal(string_param_length_));
- Definition* offset_def = LoadLocal(current_position_);
- BranchOrBacktrack(Comparison(kEQ, neg_len_def, offset_def),
- on_at_start);
-
- BindBlock(¬_at_start);
-}
-
-
-void IRRegExpMacroAssembler::CheckNotAtStart(BlockLabel* on_not_at_start) {
- TAG();
-
- // Did we start the match at the start of the string at all?
- BranchOrBacktrack(Comparison(kNE,
- LoadLocal(start_index_param_),
- Uint64Constant(0)),
- on_not_at_start);
-
- // If we did, are we still at the start of the input, i.e. is
- // (offset == string_length * -1)?
- Definition* neg_len_def =
- InstanceCall(InstanceCallDescriptor::FromToken(Token::kNEGATE),
- PushLocal(string_param_length_));
- Definition* offset_def = LoadLocal(current_position_);
- BranchOrBacktrack(Comparison(kNE, neg_len_def, offset_def),
- on_not_at_start);
-}
-
-
-void IRRegExpMacroAssembler::CheckCharacterLT(uint16_t limit,
- BlockLabel* on_less) {
- TAG();
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(current_character_),
- Uint64Constant(limit)),
- on_less);
-}
-
-
-void IRRegExpMacroAssembler::CheckGreedyLoop(BlockLabel* on_equal) {
- TAG();
-
- BlockLabel fallthrough;
-
- Definition* head = PeekStack();
- Definition* cur_pos_def = LoadLocal(current_position_);
- BranchOrBacktrack(Comparison(kNE, head, cur_pos_def),
- &fallthrough);
-
- // Pop, throwing away the value.
- Do(PopStack());
-
- BranchOrBacktrack(NULL, on_equal);
-
- BindBlock(&fallthrough);
-}
-
-
-void IRRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(
- intptr_t start_reg,
- BlockLabel* on_no_match) {
- TAG();
- ASSERT(start_reg + 1 <= registers_count_);
-
- BlockLabel fallthrough;
-
- PushArgumentInstr* end_push = PushArgument(LoadRegister(start_reg + 1));
- PushArgumentInstr* start_push = PushArgument(LoadRegister(start_reg));
- StoreLocal(capture_length_, Bind(Sub(end_push, start_push)));
-
- // The length of a capture should not be negative. This can only happen
- // if the end of the capture is unrecorded, or at a point earlier than
- // the start of the capture.
- // BranchOrBacktrack(less, on_no_match);
-
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(capture_length_),
- Uint64Constant(0)),
- on_no_match);
-
- // If length is zero, either the capture is empty or it is completely
- // uncaptured. In either case succeed immediately.
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(capture_length_),
- Uint64Constant(0)),
- &fallthrough);
-
-
- // Check that there are sufficient characters left in the input.
- PushArgumentInstr* pos_push = PushLocal(current_position_);
- PushArgumentInstr* len_push = PushLocal(capture_length_);
- BranchOrBacktrack(
- Comparison(kGT,
- InstanceCall(InstanceCallDescriptor::FromToken(Token::kADD),
- pos_push,
- len_push),
- Uint64Constant(0)),
- on_no_match);
-
- pos_push = PushLocal(current_position_);
- len_push = PushLocal(string_param_length_);
- StoreLocal(match_start_index_, Bind(Add(pos_push, len_push)));
-
- pos_push = PushArgument(LoadRegister(start_reg));
- len_push = PushLocal(string_param_length_);
- StoreLocal(capture_start_index_, Bind(Add(pos_push, len_push)));
-
- pos_push = PushLocal(match_start_index_);
- len_push = PushLocal(capture_length_);
- StoreLocal(match_end_index_, Bind(Add(pos_push, len_push)));
-
- BlockLabel success;
- if (mode_ == ASCII) {
- BlockLabel loop_increment;
- BlockLabel loop;
- BindBlock(&loop);
-
- StoreLocal(char_in_capture_, CharacterAt(capture_start_index_));
- StoreLocal(char_in_match_, CharacterAt(match_start_index_));
-
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(char_in_capture_),
- LoadLocal(char_in_match_)),
- &loop_increment);
-
- // Mismatch, try case-insensitive match (converting letters to lower-case).
- PushArgumentInstr* match_char_push = PushLocal(char_in_match_);
- PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(0x20)));
- StoreLocal(char_in_match_,
- Bind(InstanceCall(
- InstanceCallDescriptor::FromToken(Token::kBIT_OR),
- match_char_push,
- mask_push)));
-
- BlockLabel convert_capture;
- BlockLabel on_not_in_range;
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(char_in_match_),
- Uint64Constant('a')),
- &on_not_in_range);
- BranchOrBacktrack(Comparison(kGT,
- LoadLocal(char_in_match_),
- Uint64Constant('z')),
- &on_not_in_range);
- GoTo(&convert_capture);
- BindBlock(&on_not_in_range);
-
- // Latin-1: Check for values in range [224,254] but not 247.
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(char_in_match_),
- Uint64Constant(224)),
- on_no_match);
- BranchOrBacktrack(Comparison(kGT,
- LoadLocal(char_in_match_),
- Uint64Constant(254)),
- on_no_match);
-
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(char_in_match_),
- Uint64Constant(247)),
- on_no_match);
-
- // Also convert capture character.
- BindBlock(&convert_capture);
-
- PushArgumentInstr* capture_char_push = PushLocal(char_in_capture_);
- mask_push = PushArgument(Bind(Uint64Constant(0x20)));
- StoreLocal(char_in_capture_,
- Bind(InstanceCall(
- InstanceCallDescriptor::FromToken(Token::kBIT_OR),
- capture_char_push,
- mask_push)));
-
- BranchOrBacktrack(Comparison(kNE,
- LoadLocal(char_in_match_),
- LoadLocal(char_in_capture_)),
- on_no_match);
-
- BindBlock(&loop_increment);
-
- // Increment indexes into capture and match strings.
- PushArgumentInstr* index_push = PushLocal(capture_start_index_);
- PushArgumentInstr* inc_push = PushArgument(Bind(Uint64Constant(1)));
- StoreLocal(capture_start_index_, Bind(Add(index_push, inc_push)));
-
- index_push = PushLocal(match_start_index_);
- inc_push = PushArgument(Bind(Uint64Constant(1)));
- StoreLocal(match_start_index_, Bind(Add(index_push, inc_push)));
-
- // Compare to end of match, and loop if not done.
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(match_start_index_),
- LoadLocal(match_end_index_)),
- &loop);
- } else {
- ASSERT(mode_ == UC16);
-
- Value* string_value = Bind(LoadLocal(string_param_));
- Value* lhs_index_value = Bind(LoadLocal(match_start_index_));
- Value* rhs_index_value = Bind(LoadLocal(capture_start_index_));
- Value* length_value = Bind(LoadLocal(capture_length_));
-
- Definition* is_match_def =
- new(Z) CaseInsensitiveCompareUC16Instr(
- string_value,
- lhs_index_value,
- rhs_index_value,
- length_value,
- specialization_cid_);
-
- BranchOrBacktrack(Comparison(kNE, is_match_def, BoolConstant(true)),
- on_no_match);
- }
-
- BindBlock(&success);
-
- // Move current character position to position after match.
- PushArgumentInstr* match_end_push = PushLocal(match_end_index_);
- len_push = PushLocal(string_param_length_);
- StoreLocal(current_position_, Bind(Sub(match_end_push, len_push)));
-
- BindBlock(&fallthrough);
-}
-
-
-void IRRegExpMacroAssembler::CheckNotBackReference(
- intptr_t start_reg,
- BlockLabel* on_no_match) {
- TAG();
- ASSERT(start_reg + 1 <= registers_count_);
-
- BlockLabel fallthrough;
- BlockLabel success;
-
- // Find length of back-referenced capture.
- PushArgumentInstr* end_push = PushArgument(LoadRegister(start_reg + 1));
- PushArgumentInstr* start_push = PushArgument(LoadRegister(start_reg));
- StoreLocal(capture_length_, Bind(Sub(end_push, start_push)));
-
- // Fail on partial or illegal capture (start of capture after end of capture).
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(capture_length_),
- Uint64Constant(0)),
- on_no_match);
-
- // Succeed on empty capture (including no capture)
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(capture_length_),
- Uint64Constant(0)),
- &fallthrough);
-
- // Check that there are sufficient characters left in the input.
- PushArgumentInstr* pos_push = PushLocal(current_position_);
- PushArgumentInstr* len_push = PushLocal(capture_length_);
- BranchOrBacktrack(
- Comparison(kGT,
- InstanceCall(InstanceCallDescriptor::FromToken(Token::kADD),
- pos_push,
- len_push),
- Uint64Constant(0)),
- on_no_match);
-
- // Compute pointers to match string and capture string.
- pos_push = PushLocal(current_position_);
- len_push = PushLocal(string_param_length_);
- StoreLocal(match_start_index_, Bind(Add(pos_push, len_push)));
-
- pos_push = PushArgument(LoadRegister(start_reg));
- len_push = PushLocal(string_param_length_);
- StoreLocal(capture_start_index_, Bind(Add(pos_push, len_push)));
-
- pos_push = PushLocal(match_start_index_);
- len_push = PushLocal(capture_length_);
- StoreLocal(match_end_index_, Bind(Add(pos_push, len_push)));
-
- BlockLabel loop;
- BindBlock(&loop);
-
- StoreLocal(char_in_capture_, CharacterAt(capture_start_index_));
- StoreLocal(char_in_match_, CharacterAt(match_start_index_));
-
- BranchOrBacktrack(Comparison(kNE,
- LoadLocal(char_in_capture_),
- LoadLocal(char_in_match_)),
- on_no_match);
-
- // Increment indexes into capture and match strings.
- PushArgumentInstr* index_push = PushLocal(capture_start_index_);
- PushArgumentInstr* inc_push = PushArgument(Bind(Uint64Constant(1)));
- StoreLocal(capture_start_index_, Bind(Add(index_push, inc_push)));
-
- index_push = PushLocal(match_start_index_);
- inc_push = PushArgument(Bind(Uint64Constant(1)));
- StoreLocal(match_start_index_, Bind(Add(index_push, inc_push)));
-
- // Check if we have reached end of match area.
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(match_start_index_),
- LoadLocal(match_end_index_)),
- &loop);
-
- BindBlock(&success);
-
- // Move current character position to position after match.
- PushArgumentInstr* match_end_push = PushLocal(match_end_index_);
- len_push = PushLocal(string_param_length_);
- StoreLocal(current_position_, Bind(Sub(match_end_push, len_push)));
-
- BindBlock(&fallthrough);
-}
-
-
-void IRRegExpMacroAssembler::CheckNotCharacter(uint32_t c,
- BlockLabel* on_not_equal) {
- TAG();
- BranchOrBacktrack(Comparison(kNE,
- LoadLocal(current_character_),
- Uint64Constant(c)),
- on_not_equal);
-}
-
-
-void IRRegExpMacroAssembler::CheckCharacterAfterAnd(uint32_t c,
- uint32_t mask,
- BlockLabel* on_equal) {
- TAG();
-
- Definition* actual_def = LoadLocal(current_character_);
- Definition* expected_def = Uint64Constant(c);
-
- PushArgumentInstr* actual_push = PushArgument(Bind(actual_def));
- PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(mask)));
- actual_def = InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
- actual_push,
- mask_push);
-
- BranchOrBacktrack(Comparison(kEQ, actual_def, expected_def), on_equal);
-}
-
-
-void IRRegExpMacroAssembler::CheckNotCharacterAfterAnd(
- uint32_t c,
- uint32_t mask,
- BlockLabel* on_not_equal) {
- TAG();
-
- Definition* actual_def = LoadLocal(current_character_);
- Definition* expected_def = Uint64Constant(c);
-
- PushArgumentInstr* actual_push = PushArgument(Bind(actual_def));
- PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(mask)));
- actual_def = InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
- actual_push,
- mask_push);
-
- BranchOrBacktrack(Comparison(kNE, actual_def, expected_def), on_not_equal);
-}
-
-
-void IRRegExpMacroAssembler::CheckNotCharacterAfterMinusAnd(
- uint16_t c,
- uint16_t minus,
- uint16_t mask,
- BlockLabel* on_not_equal) {
- TAG();
- ASSERT(minus < Utf16::kMaxCodeUnit); // NOLINT
-
- Definition* actual_def = LoadLocal(current_character_);
- Definition* expected_def = Uint64Constant(c);
-
- PushArgumentInstr* actual_push = PushArgument(Bind(actual_def));
- PushArgumentInstr* minus_push = PushArgument(Bind(Uint64Constant(minus)));
-
- actual_push = PushArgument(Bind(Sub(actual_push, minus_push)));
- PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(mask)));
- actual_def = InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
- actual_push,
- mask_push);
-
- BranchOrBacktrack(Comparison(kNE, actual_def, expected_def), on_not_equal);
-}
-
-
-void IRRegExpMacroAssembler::CheckCharacterInRange(
- uint16_t from,
- uint16_t to,
- BlockLabel* on_in_range) {
- TAG();
- ASSERT(from <= to);
-
- // TODO(zerny): All range comparisons could be done cheaper with unsigned
- // compares. This pattern repeats in various places.
-
- BlockLabel on_not_in_range;
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(current_character_),
- Uint64Constant(from)),
- &on_not_in_range);
- BranchOrBacktrack(Comparison(kGT,
- LoadLocal(current_character_),
- Uint64Constant(to)),
- &on_not_in_range);
- BranchOrBacktrack(NULL, on_in_range);
-
- BindBlock(&on_not_in_range);
-}
-
-
-void IRRegExpMacroAssembler::CheckCharacterNotInRange(
- uint16_t from,
- uint16_t to,
- BlockLabel* on_not_in_range) {
- TAG();
- ASSERT(from <= to);
-
- BranchOrBacktrack(Comparison(kLT,
- LoadLocal(current_character_),
- Uint64Constant(from)),
- on_not_in_range);
-
- BranchOrBacktrack(Comparison(kGT,
- LoadLocal(current_character_),
- Uint64Constant(to)),
- on_not_in_range);
-}
-
-
-void IRRegExpMacroAssembler::CheckBitInTable(
- const TypedData& table,
- BlockLabel* on_bit_set) {
- TAG();
-
- PushArgumentInstr* table_push =
- PushArgument(Bind(new(Z) ConstantInstr(table)));
- PushArgumentInstr* index_push = PushLocal(current_character_);
-
- if (mode_ != ASCII || kTableMask != Symbols::kMaxOneCharCodeSymbol) {
- PushArgumentInstr* mask_push =
- PushArgument(Bind(Uint64Constant(kTableSize - 1)));
- index_push = PushArgument(
- Bind(InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
- index_push,
- mask_push)));
- }
-
- Definition* byte_def =
- InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
- table_push,
- index_push);
- Definition* zero_def = Int64Constant(0);
-
- BranchOrBacktrack(Comparison(kNE, byte_def, zero_def), on_bit_set);
-}
-
-
-bool IRRegExpMacroAssembler::CheckSpecialCharacterClass(
- uint16_t type,
- BlockLabel* on_no_match) {
- TAG();
-
- // Range checks (c in min..max) are generally implemented by an unsigned
- // (c - min) <= (max - min) check
- switch (type) {
- case 's':
- // Match space-characters
- if (mode_ == ASCII) {
- // One byte space characters are '\t'..'\r', ' ' and \u00a0.
- BlockLabel success;
- // Space (' ').
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant(' ')),
- &success);
- // Check range 0x09..0x0d.
- CheckCharacterInRange('\t', '\r', &success);
- // \u00a0 (NBSP).
- BranchOrBacktrack(Comparison(kNE,
- LoadLocal(current_character_),
- Uint64Constant(0x00a0)),
- on_no_match);
- BindBlock(&success);
- return true;
- }
- return false;
- case 'S':
- // The emitted code for generic character classes is good enough.
- return false;
- case 'd':
- // Match ASCII digits ('0'..'9')
- CheckCharacterNotInRange('0', '9', on_no_match);
- return true;
- case 'D':
- // Match non ASCII-digits
- CheckCharacterInRange('0', '9', on_no_match);
- return true;
- case '.': {
- // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant('\n')),
- on_no_match);
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant('\r')),
- on_no_match);
- if (mode_ == UC16) {
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant(0x2028)),
- on_no_match);
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant(0x2029)),
- on_no_match);
- }
- return true;
- }
- case 'w': {
- if (mode_ != ASCII) {
- // Table is 128 entries, so all ASCII characters can be tested.
- BranchOrBacktrack(Comparison(kGT,
- LoadLocal(current_character_),
- Uint64Constant('z')),
- on_no_match);
- }
-
- PushArgumentInstr* table_push =
- PushArgument(Bind(WordCharacterMapConstant()));
- PushArgumentInstr* index_push = PushLocal(current_character_);
-
- Definition* byte_def =
- InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
- table_push,
- index_push);
- Definition* zero_def = Int64Constant(0);
-
- BranchOrBacktrack(Comparison(kEQ, byte_def, zero_def), on_no_match);
-
- return true;
- }
- case 'W': {
- BlockLabel done;
- if (mode_ != ASCII) {
- // Table is 128 entries, so all ASCII characters can be tested.
- BranchOrBacktrack(Comparison(kGT,
- LoadLocal(current_character_),
- Uint64Constant('z')),
- &done);
- }
-
- // TODO(zerny): Refactor to use CheckBitInTable if possible.
-
- PushArgumentInstr* table_push =
- PushArgument(Bind(WordCharacterMapConstant()));
- PushArgumentInstr* index_push = PushLocal(current_character_);
-
- Definition* byte_def =
- InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
- table_push,
- index_push);
- Definition* zero_def = Int64Constant(0);
-
- BranchOrBacktrack(Comparison(kNE, byte_def, zero_def), on_no_match);
-
- if (mode_ != ASCII) {
- BindBlock(&done);
- }
- return true;
- }
- // Non-standard classes (with no syntactic shorthand) used internally.
- case '*':
- // Match any character.
- return true;
- case 'n': {
- // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029).
- // The opposite of '.'.
- BlockLabel success;
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant('\n')),
- &success);
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant('\r')),
- &success);
- if (mode_ == UC16) {
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant(0x2028)),
- &success);
- BranchOrBacktrack(Comparison(kEQ,
- LoadLocal(current_character_),
- Uint64Constant(0x2029)),
- &success);
- }
- BranchOrBacktrack(NULL, on_no_match);
- BindBlock(&success);
- return true;
- }
- // No custom implementation (yet): s(uint16_t), S(uint16_t).
- default:
- return false;
- }
-}
-
-
-void IRRegExpMacroAssembler::Fail() {
- TAG();
- ASSERT(FAILURE == 0); // Return value for failure is zero.
- if (!global()) {
- UNREACHABLE(); // Dart regexps are always global.
- }
- GoTo(exit_block_);
-}
-
-
-void IRRegExpMacroAssembler::IfRegisterGE(intptr_t reg,
- intptr_t comparand,
- BlockLabel* if_ge) {
- TAG();
- PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
- PushArgumentInstr* pos = PushArgument(Bind(Int64Constant(comparand)));
- BranchOrBacktrack(Comparison(kGTE, reg_push, pos), if_ge);
-}
-
-
-void IRRegExpMacroAssembler::IfRegisterLT(intptr_t reg,
- intptr_t comparand,
- BlockLabel* if_lt) {
- TAG();
- PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
- PushArgumentInstr* pos = PushArgument(Bind(Int64Constant(comparand)));
- BranchOrBacktrack(Comparison(kLT, reg_push, pos), if_lt);
-}
-
-
-void IRRegExpMacroAssembler::IfRegisterEqPos(intptr_t reg,
- BlockLabel* if_eq) {
- TAG();
- PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
- PushArgumentInstr* pos = PushArgument(Bind(LoadLocal(current_position_)));
- BranchOrBacktrack(Comparison(kEQ, reg_push, pos), if_eq);
-}
-
-
-RegExpMacroAssembler::IrregexpImplementation
- IRRegExpMacroAssembler::Implementation() {
- return kIRImplementation;
-}
-
-
-void IRRegExpMacroAssembler::LoadCurrentCharacter(intptr_t cp_offset,
- BlockLabel* on_end_of_input,
- bool check_bounds,
- intptr_t characters) {
- TAG();
- ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
- ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
- if (check_bounds) {
- CheckPosition(cp_offset + characters - 1, on_end_of_input);
- }
- LoadCurrentCharacterUnchecked(cp_offset, characters);
-}
-
-
-void IRRegExpMacroAssembler::PopCurrentPosition() {
- TAG();
- StoreLocal(current_position_, Bind(PopStack()));
-}
-
-
-void IRRegExpMacroAssembler::PopRegister(intptr_t reg) {
- TAG();
- ASSERT(reg < registers_count_);
- PushArgumentInstr* registers_push = PushLocal(registers_);
- PushArgumentInstr* index_push = PushRegisterIndex(reg);
- PushArgumentInstr* pop_push = PushArgument(Bind(PopStack()));
- StoreRegister(registers_push, index_push, pop_push);
-}
-
-
-void IRRegExpMacroAssembler::PushStack(Definition *definition) {
- PushArgumentInstr* stack_push = PushLocal(stack_);
- PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
- StoreLocal(stack_pointer_,
- Bind(Add(stack_pointer_push,
- PushArgument(Bind(Uint64Constant(1))))));
- stack_pointer_push = PushLocal(stack_pointer_);
- // TODO(zerny): bind value and push could break stack discipline.
- PushArgumentInstr* value_push = PushArgument(Bind(definition));
- Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
- stack_push,
- stack_pointer_push,
- value_push));
-}
-
-
-Definition* IRRegExpMacroAssembler::PopStack() {
- PushArgumentInstr* stack_push = PushLocal(stack_);
- PushArgumentInstr* stack_pointer_push1 = PushLocal(stack_pointer_);
- PushArgumentInstr* stack_pointer_push2 = PushLocal(stack_pointer_);
- StoreLocal(stack_pointer_,
- Bind(Sub(stack_pointer_push2,
- PushArgument(Bind(Uint64Constant(1))))));
- return InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
- stack_push,
- stack_pointer_push1);
-}
-
-
-Definition* IRRegExpMacroAssembler::PeekStack() {
- PushArgumentInstr* stack_push = PushLocal(stack_);
- PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
- return InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
- stack_push,
- stack_pointer_push);
-}
-
-
-// Pushes the location corresponding to label to the backtracking stack.
-void IRRegExpMacroAssembler::PushBacktrack(BlockLabel* label) {
- TAG();
-
- // Ensure that targets of indirect jumps are never accessed through a
- // normal control flow instructions by creating a new block for each backtrack
- // target.
- IndirectEntryInstr* indirect_target = IndirectWithJoinGoto(label->block());
-
- // Add a fake edge from the graph entry for data flow analysis.
- entry_block_->AddIndirectEntry(indirect_target);
-
- ConstantInstr* offset = Uint64Constant(indirect_target->indirect_id());
- PushStack(offset);
- CheckStackLimit();
-}
-
-
-void IRRegExpMacroAssembler::PushCurrentPosition() {
- TAG();
- PushStack(LoadLocal(current_position_));
-}
-
-
-void IRRegExpMacroAssembler::PushRegister(intptr_t reg) {
- TAG();
- // TODO(zerny): Refactor PushStack so it can be reused here.
- PushArgumentInstr* stack_push = PushLocal(stack_);
- PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
- StoreLocal(stack_pointer_,
- Bind(Add(stack_pointer_push,
- PushArgument(Bind(Uint64Constant(1))))));
- stack_pointer_push = PushLocal(stack_pointer_);
- // TODO(zerny): bind value and push could break stack discipline.
- PushArgumentInstr* value_push = PushArgument(LoadRegister(reg));
- Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
- stack_push,
- stack_pointer_push,
- value_push));
- CheckStackLimit();
-}
-
-
-// Checks that (stack.capacity - stack_limit_slack) > stack_pointer.
-// This ensures that up to stack_limit_slack stack pushes can be
-// done without exhausting the stack space. If the check fails the
-// stack will be grown.
-void IRRegExpMacroAssembler::CheckStackLimit() {
- TAG();
- PushArgumentInstr* stack_push = PushLocal(stack_);
- PushArgumentInstr* length_push = PushArgument(Bind(InstanceCall(
- InstanceCallDescriptor(
- String::ZoneHandle(Field::GetterSymbol(Symbols::Length()))),
- stack_push)));
- PushArgumentInstr* capacity_push = PushArgument(Bind(Sub(
- length_push,
- PushArgument(Bind(Uint64Constant(stack_limit_slack()))))));
- PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
- BranchInstr* branch = new(Z) BranchInstr(
- Comparison(kGT, capacity_push, stack_pointer_push));
- CloseBlockWith(branch);
-
- BlockLabel grow_stack;
- BlockLabel fallthrough;
- *branch->true_successor_address() =
- TargetWithJoinGoto(fallthrough.block());
- *branch->false_successor_address() =
- TargetWithJoinGoto(grow_stack.block());
-
- BindBlock(&grow_stack);
- GrowStack();
-
- BindBlock(&fallthrough);
-}
-
-
-void IRRegExpMacroAssembler::GrowStack() {
- TAG();
- Value* cell = Bind(new(Z) ConstantInstr(stack_array_cell_));
- StoreLocal(stack_, Bind(new(Z) GrowRegExpStackInstr(cell)));
-}
-
-
-void IRRegExpMacroAssembler::ReadCurrentPositionFromRegister(intptr_t reg) {
- TAG();
- StoreLocal(current_position_, LoadRegister(reg));
-}
-
-// Resets the tip of the stack to the value stored in reg.
-void IRRegExpMacroAssembler::ReadStackPointerFromRegister(intptr_t reg) {
- TAG();
- ASSERT(reg < registers_count_);
- StoreLocal(stack_pointer_, LoadRegister(reg));
-}
-
-void IRRegExpMacroAssembler::SetCurrentPositionFromEnd(intptr_t by) {
- TAG();
-
- BlockLabel after_position;
-
- Definition* cur_pos_def = LoadLocal(current_position_);
- Definition* by_value_def = Int64Constant(-by);
-
- BranchOrBacktrack(Comparison(kGTE, cur_pos_def, by_value_def),
- &after_position);
-
- StoreLocal(current_position_, Bind(Int64Constant(-by)));
-
- // On RegExp code entry (where this operation is used), the character before
- // the current position is expected to be already loaded.
- // We have advanced the position, so it's safe to read backwards.
- LoadCurrentCharacterUnchecked(-1, 1);
-
- BindBlock(&after_position);
-}
-
-
-void IRRegExpMacroAssembler::SetRegister(intptr_t reg, intptr_t to) {
- TAG();
- // Reserved for positions!
- ASSERT(reg >= saved_registers_count_);
- StoreRegister(reg, to);
-}
-
-
-bool IRRegExpMacroAssembler::Succeed() {
- TAG();
- GoTo(success_block_);
- return global();
-}
-
-
-void IRRegExpMacroAssembler::WriteCurrentPositionToRegister(
- intptr_t reg, intptr_t cp_offset) {
- TAG();
-
- PushArgumentInstr* registers_push = PushLocal(registers_);
- PushArgumentInstr* index_push = PushRegisterIndex(reg);
- PushArgumentInstr* pos_push = PushLocal(current_position_);
- PushArgumentInstr* off_push = PushArgument(Bind(Int64Constant(cp_offset)));
- PushArgumentInstr* neg_off_push = PushArgument(Bind(Add(pos_push, off_push)));
- // Push the negative offset; these are converted to positive string positions
- // within the success block.
- StoreRegister(registers_push, index_push, neg_off_push);
-}
-
-
-void IRRegExpMacroAssembler::ClearRegisters(
- intptr_t reg_from, intptr_t reg_to) {
- TAG();
-
- ASSERT(reg_from <= reg_to);
-
- // In order to clear registers to a final result value of -1, set them to
- // (-1 - string length), the offset of -1 from the end of the string.
-
- for (intptr_t reg = reg_from; reg <= reg_to; reg++) {
- PushArgumentInstr* registers_push = PushLocal(registers_);
- PushArgumentInstr* index_push = PushRegisterIndex(reg);
- PushArgumentInstr* minus_one_push =
- PushArgument(Bind(Int64Constant(-1)));
- PushArgumentInstr* length_push = PushLocal(string_param_length_);
- PushArgumentInstr* value_push =
- PushArgument(Bind(Sub(minus_one_push, length_push)));
- StoreRegister(registers_push, index_push, value_push);
- }
-}
-
-
-void IRRegExpMacroAssembler::WriteStackPointerToRegister(intptr_t reg) {
- TAG();
-
- PushArgumentInstr* registers_push = PushLocal(registers_);
- PushArgumentInstr* index_push = PushRegisterIndex(reg);
- PushArgumentInstr* tip_push = PushLocal(stack_pointer_);
- StoreRegister(registers_push, index_push, tip_push);
-}
-
-
-// Private methods:
-
-
-void IRRegExpMacroAssembler::CheckPosition(intptr_t cp_offset,
- BlockLabel* on_outside_input) {
- TAG();
- Definition* curpos_def = LoadLocal(current_position_);
- Definition* cp_off_def = Int64Constant(-cp_offset);
-
- // If (current_position_ < -cp_offset), we are in bounds.
- // Remember, current_position_ is a negative offset from the string end.
-
- BranchOrBacktrack(Comparison(kGTE, curpos_def, cp_off_def),
- on_outside_input);
-}
-
-
-void IRRegExpMacroAssembler::BranchOrBacktrack(
- ComparisonInstr* comparison,
- BlockLabel* true_successor) {
- if (comparison == NULL) { // No condition
- if (true_successor == NULL) {
- Backtrack();
- return;
- }
- GoTo(true_successor);
- return;
- }
-
- // If no successor block has been passed in, backtrack.
- JoinEntryInstr* true_successor_block = backtrack_block_;
- if (true_successor != NULL) {
- true_successor->SetLinked();
- true_successor_block = true_successor->block();
- }
- ASSERT(true_successor_block != NULL);
-
- // If the condition is not true, fall through to a new block.
- BlockLabel fallthrough;
-
- BranchInstr* branch = new(Z) BranchInstr(comparison);
- *branch->true_successor_address() =
- TargetWithJoinGoto(true_successor_block);
- *branch->false_successor_address() =
- TargetWithJoinGoto(fallthrough.block());
-
- CloseBlockWith(branch);
- BindBlock(&fallthrough);
-}
-
-
-TargetEntryInstr* IRRegExpMacroAssembler::TargetWithJoinGoto(
- JoinEntryInstr* dst) {
- TargetEntryInstr* target = new(Z) TargetEntryInstr(
- block_id_.Alloc(), kInvalidTryIndex);
- blocks_.Add(target);
-
- target->AppendInstruction(new(Z) GotoInstr(dst));
-
- return target;
-}
-
-
-IndirectEntryInstr* IRRegExpMacroAssembler::IndirectWithJoinGoto(
- JoinEntryInstr* dst) {
- IndirectEntryInstr* target = new(Z) IndirectEntryInstr(
- block_id_.Alloc(), indirect_id_.Alloc(), kInvalidTryIndex);
- blocks_.Add(target);
-
- target->AppendInstruction(new(Z) GotoInstr(dst));
-
- return target;
-}
-
-
-void IRRegExpMacroAssembler::CheckPreemption() {
- TAG();
- AppendInstruction(new(Z) CheckStackOverflowInstr(kNoSourcePos, 0));
-}
-
-
-Definition* IRRegExpMacroAssembler::Add(
- PushArgumentInstr* lhs,
- PushArgumentInstr* rhs) {
- return InstanceCall(InstanceCallDescriptor::FromToken(Token::kADD), lhs, rhs);
-}
-
-
-Definition* IRRegExpMacroAssembler::Sub(
- PushArgumentInstr* lhs,
- PushArgumentInstr* rhs) {
- return InstanceCall(InstanceCallDescriptor::FromToken(Token::kSUB), lhs, rhs);
-}
-
-
-void IRRegExpMacroAssembler::LoadCurrentCharacterUnchecked(
- intptr_t cp_offset, intptr_t characters) {
- TAG();
-
- ASSERT(characters == 1 || CanReadUnaligned());
- if (mode_ == ASCII) {
- ASSERT(characters == 1 || characters == 2 || characters == 4);
- } else {
- ASSERT(mode_ == UC16);
- ASSERT(characters == 1 || characters == 2);
- }
-
- // Calculate the addressed string index as:
- // cp_offset + current_position_ + string_param_length_
- // TODO(zerny): Avoid generating 'add' instance-calls here.
- PushArgumentInstr* off_arg =
- PushArgument(Bind(Int64Constant(cp_offset)));
- PushArgumentInstr* pos_arg =
- PushArgument(BindLoadLocal(*current_position_));
- PushArgumentInstr* off_pos_arg =
- PushArgument(Bind(Add(off_arg, pos_arg)));
- PushArgumentInstr* len_arg =
- PushArgument(BindLoadLocal(*string_param_length_));
- // Index is stored in a temporary local so that we can later load it safely.
- StoreLocal(index_temp_, Bind(Add(off_pos_arg, len_arg)));
-
- // Load and store the code units.
- Value* code_unit_value = LoadCodeUnitsAt(index_temp_, characters);
- StoreLocal(current_character_, code_unit_value);
- PRINT(PushLocal(current_character_));
-}
-
-
-Value* IRRegExpMacroAssembler::CharacterAt(LocalVariable* index) {
- return LoadCodeUnitsAt(index, 1);
-}
-
-
-Value* IRRegExpMacroAssembler::LoadCodeUnitsAt(LocalVariable* index,
- intptr_t characters) {
- // Bind the pattern as the load receiver.
- Value* pattern_val = BindLoadLocal(*string_param_);
- if (RawObject::IsExternalStringClassId(specialization_cid_)) {
- // The data of an external string is stored through two indirections.
- intptr_t external_offset = 0;
- intptr_t data_offset = 0;
- if (specialization_cid_ == kExternalOneByteStringCid) {
- external_offset = ExternalOneByteString::external_data_offset();
- data_offset = RawExternalOneByteString::ExternalData::data_offset();
- } else if (specialization_cid_ == kExternalTwoByteStringCid) {
- external_offset = ExternalTwoByteString::external_data_offset();
- data_offset = RawExternalTwoByteString::ExternalData::data_offset();
- } else {
- UNREACHABLE();
- }
- // This pushes untagged values on the stack which are immediately consumed:
- // the first value is consumed to obtain the second value which is consumed
- // by LoadCodeUnitsAtInstr below.
- Value* external_val =
- Bind(new(Z) LoadUntaggedInstr(pattern_val, external_offset));
- pattern_val =
- Bind(new(Z) LoadUntaggedInstr(external_val, data_offset));
- }
-
- // Here pattern_val might be untagged so this must not trigger a GC.
- Value* index_val = BindLoadLocal(*index);
-
- return Bind(new(Z) LoadCodeUnitsInstr(
- pattern_val,
- index_val,
- characters,
- specialization_cid_,
- Scanner::kNoSourcePos));
-}
-
-
-#undef __
-
} // namespace dart
diff --git a/runtime/vm/regexp_assembler.h b/runtime/vm/regexp_assembler.h
index 88aa06b..821f054 100644
--- a/runtime/vm/regexp_assembler.h
+++ b/runtime/vm/regexp_assembler.h
@@ -17,24 +17,13 @@
/// Convenience wrapper around a BlockEntryInstr pointer.
class BlockLabel : public ValueObject {
+ // Used by the IR assembler.
public:
BlockLabel()
: block_(new JoinEntryInstr(-1, -1)),
is_bound_(false),
- is_linked_(false) { }
-
- BlockLabel(const BlockLabel& that)
- : ValueObject(),
- block_(that.block_),
- is_bound_(that.is_bound_),
- is_linked_(that.is_linked_) { }
-
- BlockLabel& operator=(const BlockLabel& that) {
- block_ = that.block_;
- is_bound_ = that.is_bound_;
- is_linked_ = that.is_linked_;
- return *this;
- }
+ is_linked_(false),
+ pos_(-1) { }
JoinEntryInstr* block() const { return block_; }
@@ -60,6 +49,41 @@
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(); }
+
+ void Unuse() {
+ pos_ = 0;
+ is_bound_ = false;
+ is_linked_ = false;
+ }
+
+ void bind_to(intptr_t pos) {
+ pos_ = pos;
+ is_bound_ = true;
+ is_linked_ = false;
+ ASSERT(is_bound());
+ }
+
+ void link_to(intptr_t pos) {
+ pos_ = pos;
+ is_bound_ = false;
+ is_linked_ = true;
+ ASSERT(is_linked());
+ }
+
+ private:
+ intptr_t pos_;
};
@@ -81,6 +105,7 @@
};
enum IrregexpImplementation {
+ kBytecodeImplementation,
kIRImplementation
};
@@ -217,433 +242,6 @@
Zone* zone_;
};
-
-class IRRegExpMacroAssembler : public RegExpMacroAssembler {
- public:
- // Type of input string to generate code for.
- enum Mode { ASCII = 1, UC16 = 2 };
-
- // Result of calling generated native RegExp code.
- // RETRY: Something significant changed during execution, and the matching
- // should be retried from scratch.
- // EXCEPTION: Something failed during execution. If no exception has been
- // thrown, it's an internal out-of-memory, and the caller should
- // throw the exception.
- // FAILURE: Matching failed.
- // SUCCESS: Matching succeeded, and the output array has been filled with
- // capture positions.
- enum Result { RETRY = -2, EXCEPTION = -1, FAILURE = 0, SUCCESS = 1 };
-
- IRRegExpMacroAssembler(intptr_t specialization_cid,
- intptr_t capture_count,
- const ParsedFunction* parsed_function,
- const ZoneGrowableArray<const ICData*>& ic_data_array,
- Zone* zone);
- virtual ~IRRegExpMacroAssembler();
-
- virtual bool CanReadUnaligned();
-
- // Compares two-byte strings case insensitively.
- // Called from generated RegExp code.
- static RawBool* CaseInsensitiveCompareUC16(
- RawString* str_raw,
- RawSmi* lhs_index_raw,
- RawSmi* rhs_index_raw,
- RawSmi* length_raw);
-
- static RawArray* Execute(const Function& function,
- const String& input,
- const Smi& start_offset,
- Zone* zone);
-
- virtual bool IsClosed() const { return (current_instruction_ == NULL); }
-
- virtual intptr_t stack_limit_slack();
- virtual void AdvanceCurrentPosition(intptr_t by);
- virtual void AdvanceRegister(intptr_t reg, intptr_t by);
- virtual void Backtrack();
- virtual void BindBlock(BlockLabel* label);
- virtual void CheckAtStart(BlockLabel* on_at_start);
- virtual void CheckCharacter(uint32_t c, BlockLabel* on_equal);
- virtual void CheckCharacterAfterAnd(uint32_t c,
- uint32_t mask,
- BlockLabel* on_equal);
- virtual void CheckCharacterGT(uint16_t limit, BlockLabel* on_greater);
- virtual void CheckCharacterLT(uint16_t limit, BlockLabel* on_less);
- // A "greedy loop" is a loop that is both greedy and with a simple
- // body. It has a particularly simple implementation.
- virtual void CheckGreedyLoop(BlockLabel* on_tos_equals_current_position);
- virtual void CheckNotAtStart(BlockLabel* on_not_at_start);
- virtual void CheckNotBackReference(intptr_t start_reg,
- BlockLabel* on_no_match);
- virtual void CheckNotBackReferenceIgnoreCase(intptr_t start_reg,
- BlockLabel* on_no_match);
- virtual void CheckNotCharacter(uint32_t c, BlockLabel* on_not_equal);
- virtual void CheckNotCharacterAfterAnd(uint32_t c,
- uint32_t mask,
- BlockLabel* on_not_equal);
- virtual void CheckNotCharacterAfterMinusAnd(uint16_t c,
- uint16_t minus,
- uint16_t mask,
- BlockLabel* on_not_equal);
- virtual void CheckCharacterInRange(uint16_t from,
- uint16_t to,
- BlockLabel* on_in_range);
- virtual void CheckCharacterNotInRange(uint16_t from,
- uint16_t to,
- BlockLabel* on_not_in_range);
- virtual void CheckBitInTable(const TypedData& table, BlockLabel* on_bit_set);
-
- // Checks whether the given offset from the current position is before
- // the end of the string.
- virtual void CheckPosition(intptr_t cp_offset, BlockLabel* on_outside_input);
- virtual bool CheckSpecialCharacterClass(
- uint16_t type, BlockLabel* on_no_match);
- virtual void Fail();
- virtual void IfRegisterGE(intptr_t reg,
- intptr_t comparand, BlockLabel* if_ge);
- virtual void IfRegisterLT(intptr_t reg,
- intptr_t comparand, BlockLabel* if_lt);
- virtual void IfRegisterEqPos(intptr_t reg, BlockLabel* if_eq);
- virtual IrregexpImplementation Implementation();
- virtual void GoTo(BlockLabel* to);
- virtual void LoadCurrentCharacter(intptr_t cp_offset,
- BlockLabel* on_end_of_input,
- bool check_bounds = true,
- intptr_t characters = 1);
- virtual void PopCurrentPosition();
- virtual void PopRegister(intptr_t register_index);
- virtual void Print(const char* str);
- virtual void PushBacktrack(BlockLabel* label);
- virtual void PushCurrentPosition();
- virtual void PushRegister(intptr_t register_index);
- virtual void ReadCurrentPositionFromRegister(intptr_t reg);
- virtual void ReadStackPointerFromRegister(intptr_t reg);
- virtual void SetCurrentPositionFromEnd(intptr_t by);
- virtual void SetRegister(intptr_t register_index, intptr_t to);
- virtual bool Succeed();
- virtual void WriteCurrentPositionToRegister(intptr_t reg, intptr_t cp_offset);
- virtual void ClearRegisters(intptr_t reg_from, intptr_t reg_to);
- virtual void WriteStackPointerToRegister(intptr_t reg);
-
- virtual void PrintBlocks();
-
- IndirectGotoInstr* backtrack_goto() const { return backtrack_goto_; }
- GraphEntryInstr* graph_entry() const { return entry_block_; }
-
- intptr_t num_stack_locals() const { return local_id_.Count(); }
- intptr_t num_blocks() const { return block_id_.Count(); }
-
- // Generate a dispatch block implementing backtracking. Must be done after
- // graph construction.
- void GenerateBacktrackBlock();
-
- // Allocate the actual registers array once its size is known. Must be done
- // after graph construction.
- void FinalizeRegistersArray();
-
- private:
- // Generate the contents of preset blocks. The entry block is the entry point
- // of the generated code.
- void GenerateEntryBlock();
- // Copies capture indices into the result area and returns true.
- void GenerateSuccessBlock();
- // Returns false.
- void GenerateExitBlock();
-
- enum ComparisonKind {
- kEQ,
- kNE,
- kLT,
- kGT,
- kLTE,
- kGTE,
- };
-
- struct InstanceCallDescriptor {
- // Standard (i.e. most non-Smi) functions.
- explicit InstanceCallDescriptor(const String& name)
- : name(name),
- token_kind(Token::kILLEGAL),
- checked_argument_count(1) { }
-
- InstanceCallDescriptor(const String& name,
- Token::Kind token_kind,
- intptr_t checked_argument_count)
- : name(name),
- token_kind(token_kind),
- checked_argument_count(checked_argument_count) { }
-
- // Special cases for Smi and indexing functions.
- static InstanceCallDescriptor FromToken(Token::Kind token_kind) {
- switch (token_kind) {
- case Token::kEQ: return InstanceCallDescriptor(
- Symbols::EqualOperator(), token_kind, 2);
- case Token::kADD: return InstanceCallDescriptor(
- Symbols::Plus(), token_kind, 2);
- case Token::kSUB: return InstanceCallDescriptor(
- Symbols::Minus(), token_kind, 2);
- case Token::kBIT_OR: return InstanceCallDescriptor(
- Symbols::BitOr(), token_kind, 2);
- case Token::kBIT_AND: return InstanceCallDescriptor(
- Symbols::BitAnd(), token_kind, 2);
- case Token::kLT: return InstanceCallDescriptor(
- Symbols::LAngleBracket(), token_kind, 2);
- case Token::kLTE: return InstanceCallDescriptor(
- Symbols::LessEqualOperator(), token_kind, 2);
- case Token::kGT: return InstanceCallDescriptor(
- Symbols::RAngleBracket(), token_kind, 2);
- case Token::kGTE: return InstanceCallDescriptor(
- Symbols::GreaterEqualOperator(), token_kind, 2);
- case Token::kNEGATE: return InstanceCallDescriptor(
- Symbols::UnaryMinus(), token_kind, 1);
- case Token::kINDEX: return InstanceCallDescriptor(
- Symbols::IndexToken(), token_kind, 2);
- case Token::kASSIGN_INDEX: return InstanceCallDescriptor(
- Symbols::AssignIndexToken(), token_kind, 2);
- default:
- UNREACHABLE();
- }
- UNREACHABLE();
- return InstanceCallDescriptor(Symbols::Empty());
- }
-
- const String& name;
- Token::Kind token_kind;
- intptr_t checked_argument_count;
- };
-
- LocalVariable* Local(const String& name);
- LocalVariable* Parameter(const String& name, intptr_t index) const;
-
- ConstantInstr* Int64Constant(int64_t value) const;
- ConstantInstr* Uint64Constant(uint64_t value) const;
- ConstantInstr* BoolConstant(bool value) const;
- ConstantInstr* StringConstant(const char* value) const;
-
- // The word character map static member of the RegExp class.
- // Byte map of one byte characters with a 0xff if the character is a word
- // character (digit, letter or underscore) and 0x00 otherwise.
- // Used by generated RegExp code.
- ConstantInstr* WordCharacterMapConstant() const;
-
- ComparisonInstr* Comparison(ComparisonKind kind,
- PushArgumentInstr* lhs,
- PushArgumentInstr* rhs);
- ComparisonInstr* Comparison(ComparisonKind kind,
- Definition* lhs,
- Definition* rhs);
-
- InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc,
- PushArgumentInstr* arg1) const;
- InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc,
- PushArgumentInstr* arg1,
- PushArgumentInstr* arg2) const;
- InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc,
- PushArgumentInstr* arg1,
- PushArgumentInstr* arg2,
- PushArgumentInstr* arg3) const;
- InstanceCallInstr* InstanceCall(
- const InstanceCallDescriptor& desc,
- ZoneGrowableArray<PushArgumentInstr*>* arguments) const;
-
- StaticCallInstr* StaticCall(const Function& function) const;
- StaticCallInstr* StaticCall(const Function& function,
- PushArgumentInstr* arg1) const;
- StaticCallInstr* StaticCall(const Function& function,
- PushArgumentInstr* arg1,
- PushArgumentInstr* arg2) const;
- StaticCallInstr* StaticCall(
- const Function& function,
- ZoneGrowableArray<PushArgumentInstr*>* arguments) const;
-
- // Creates a new block consisting simply of a goto to dst.
- TargetEntryInstr* TargetWithJoinGoto(JoinEntryInstr* dst);
- IndirectEntryInstr* IndirectWithJoinGoto(JoinEntryInstr* dst);
-
- // Adds, respectively subtracts lhs and rhs and returns the result.
- Definition* Add(PushArgumentInstr* lhs, PushArgumentInstr* rhs);
- Definition* Sub(PushArgumentInstr* lhs, PushArgumentInstr* rhs);
-
- LoadLocalInstr* LoadLocal(LocalVariable* local) const;
- void StoreLocal(LocalVariable* local, Value* value);
-
- PushArgumentInstr* PushArgument(Value* value);
- PushArgumentInstr* PushLocal(LocalVariable* local);
-
- PushArgumentInstr* PushRegisterIndex(intptr_t reg);
- Value* LoadRegister(intptr_t reg);
- void StoreRegister(intptr_t reg, intptr_t value);
- void StoreRegister(PushArgumentInstr* registers,
- PushArgumentInstr* index,
- PushArgumentInstr* value);
-
- // Load a number of characters at the given offset from the
- // current position, into the current-character register.
- void LoadCurrentCharacterUnchecked(intptr_t cp_offset,
- intptr_t character_count);
-
- // Returns the character within the passed string at the specified index.
- Value* CharacterAt(LocalVariable* index);
-
- // Load a number of characters starting from index in the pattern string.
- Value* LoadCodeUnitsAt(LocalVariable* index, intptr_t character_count);
-
- // Check whether preemption has been requested.
- void CheckPreemption();
-
- // Byte size of chars in the string to match (decided by the Mode argument)
- inline intptr_t char_size() { return static_cast<int>(mode_); }
-
- // Equivalent to a conditional branch to the label, unless the label
- // is NULL, in which case it is a conditional Backtrack.
- void BranchOrBacktrack(ComparisonInstr* comparison,
- BlockLabel* true_successor);
-
- // Set up all local variables and parameters.
- void InitializeLocals();
-
- // Allocates a new local, and returns the appropriate id for placing it
- // on the stack.
- intptr_t GetNextLocalIndex();
-
- // We never have any copied parameters.
- intptr_t num_copied_params() const {
- return 0;
- }
-
- // Return the position register at the specified index, creating it if
- // necessary. Note that the number of such registers can exceed the amount
- // required by the number of output captures.
- LocalVariable* position_register(intptr_t index);
-
- void set_current_instruction(Instruction* instruction);
-
- // The following functions are responsible for appending instructions
- // to the current instruction in various ways. The most simple one
- // is AppendInstruction, which simply appends an instruction and performs
- // bookkeeping.
- void AppendInstruction(Instruction* instruction);
- // Similar to AppendInstruction, but closes the current block by
- // setting current_instruction_ to NULL.
- void CloseBlockWith(Instruction* instruction);
- // Appends definition and allocates a temp index for the result.
- Value* Bind(Definition* definition);
- // Loads and binds a local variable.
- Value* BindLoadLocal(const LocalVariable& local);
-
- // Appends the definition.
- void Do(Definition* definition);
- // Closes the current block with a jump to the specified block.
- void GoTo(JoinEntryInstr* to);
-
- // Accessors for our local stack_.
- void PushStack(Definition* definition);
- Definition* PopStack();
- Definition* PeekStack();
- void CheckStackLimit();
- void GrowStack();
-
- // Prints the specified argument. Used for debugging.
- void Print(PushArgumentInstr* argument);
-
- // A utility class tracking ids of various objects such as blocks, temps, etc.
- class IdAllocator : public ValueObject {
- public:
- IdAllocator() : next_id(0) { }
-
- intptr_t Count() const { return next_id; }
- intptr_t Alloc(intptr_t count = 1) {
- ASSERT(count >= 0);
- intptr_t current_id = next_id;
- next_id += count;
- return current_id;
- }
- void Dealloc(intptr_t count = 1) {
- ASSERT(count <= next_id);
- next_id -= count;
- }
-
- private:
- intptr_t next_id;
- };
-
- // Which mode to generate code for (ASCII or UC16).
- Mode mode_;
-
- // Which specific string class to generate code for.
- intptr_t specialization_cid_;
-
- // Block entries used internally.
- GraphEntryInstr* entry_block_;
- JoinEntryInstr* start_block_;
- JoinEntryInstr* success_block_;
- JoinEntryInstr* exit_block_;
-
- // Shared backtracking block.
- JoinEntryInstr* backtrack_block_;
- // Single indirect goto instruction which performs all backtracking.
- IndirectGotoInstr* backtrack_goto_;
-
- const ParsedFunction* parsed_function_;
- const ZoneGrowableArray<const ICData*>& ic_data_array_;
-
- // All created blocks are contained within this set. Used for printing
- // the generated code.
- GrowableArray<BlockEntryInstr*> blocks_;
-
- // The current instruction to link to when new code is emitted.
- Instruction* current_instruction_;
-
- // A list, acting as the runtime stack for both backtrack locations and
- // stored positions within the string.
- LocalVariable* stack_;
- LocalVariable* stack_pointer_;
-
- // Stores the current character within the string.
- LocalVariable* current_character_;
-
- // Stores the current location within the string as a negative offset
- // from the end of the string.
- LocalVariable* current_position_;
-
- // The string being processed, passed as a function parameter.
- LocalVariable* string_param_;
-
- // Stores the length of string_param_.
- LocalVariable* string_param_length_;
-
- // The start index within the string, passed as a function parameter.
- LocalVariable* start_index_param_;
-
- // An assortment of utility variables.
- LocalVariable* capture_length_;
- LocalVariable* match_start_index_;
- LocalVariable* capture_start_index_;
- LocalVariable* match_end_index_;
- LocalVariable* char_in_capture_;
- LocalVariable* char_in_match_;
- LocalVariable* index_temp_;
-
- LocalVariable* result_;
-
- // Stored positions containing group bounds. Generated as needed.
- LocalVariable* registers_;
- intptr_t registers_count_;
- const intptr_t saved_registers_count_;
-
- // The actual array objects used for the stack and registers.
- Array& stack_array_cell_;
- TypedData& registers_array_;
-
- IdAllocator block_id_;
- IdAllocator temp_id_;
- IdAllocator arg_id_;
- IdAllocator local_id_;
- IdAllocator indirect_id_;
-};
-
-
} // namespace dart
#endif // VM_REGEXP_ASSEMBLER_H_
diff --git a/runtime/vm/regexp_assembler_bytecode.cc b/runtime/vm/regexp_assembler_bytecode.cc
new file mode 100644
index 0000000..193616c
--- /dev/null
+++ b/runtime/vm/regexp_assembler_bytecode.cc
@@ -0,0 +1,594 @@
+// Copyright (c) 2015, 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/regexp_assembler_bytecode.h"
+
+#include "vm/regexp_assembler_bytecode_inl.h"
+#include "vm/exceptions.h"
+#include "vm/object_store.h"
+#include "vm/regexp_bytecodes.h"
+#include "vm/regexp_assembler.h"
+#include "vm/regexp.h"
+#include "vm/regexp_parser.h"
+#include "vm/regexp_interpreter.h"
+
+namespace dart {
+
+BytecodeRegExpMacroAssembler::BytecodeRegExpMacroAssembler(
+ ZoneGrowableArray<uint8_t>* buffer,
+ Zone* zone)
+ : RegExpMacroAssembler(zone),
+ buffer_(buffer),
+ pc_(0),
+ advance_current_end_(kInvalidPC) { }
+
+
+BytecodeRegExpMacroAssembler::~BytecodeRegExpMacroAssembler() {
+ if (backtrack_.is_linked()) backtrack_.Unuse();
+}
+
+
+BytecodeRegExpMacroAssembler::IrregexpImplementation
+BytecodeRegExpMacroAssembler::Implementation() {
+ return kBytecodeImplementation;
+}
+
+
+void BytecodeRegExpMacroAssembler::BindBlock(BlockLabel* l) {
+ advance_current_end_ = kInvalidPC;
+ ASSERT(!l->is_bound());
+ if (l->is_linked()) {
+ intptr_t pos = l->pos();
+ while (pos != 0) {
+ intptr_t fixup = pos;
+ pos = *reinterpret_cast<int32_t*>(buffer_->data() + fixup);
+ *reinterpret_cast<uint32_t*>(buffer_->data() + fixup) = pc_;
+ }
+ }
+ l->bind_to(pc_);
+}
+
+
+void BytecodeRegExpMacroAssembler::EmitOrLink(BlockLabel* l) {
+ if (l == NULL) l = &backtrack_;
+ if (l->is_bound()) {
+ Emit32(l->pos());
+ } else {
+ int pos = 0;
+ if (l->is_linked()) {
+ pos = l->pos();
+ }
+ l->link_to(pc_);
+ Emit32(pos);
+ }
+}
+
+
+void BytecodeRegExpMacroAssembler::PopRegister(intptr_t register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_POP_REGISTER, register_index);
+}
+
+
+void BytecodeRegExpMacroAssembler::PushRegister(intptr_t register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_PUSH_REGISTER, register_index);
+}
+
+
+void BytecodeRegExpMacroAssembler::WriteCurrentPositionToRegister(
+ intptr_t register_index, intptr_t cp_offset) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER_TO_CP, register_index);
+ Emit32(cp_offset); // Current position offset.
+}
+
+
+void BytecodeRegExpMacroAssembler::ClearRegisters(intptr_t reg_from,
+ intptr_t reg_to) {
+ ASSERT(reg_from <= reg_to);
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ SetRegister(reg, -1);
+ }
+}
+
+
+void BytecodeRegExpMacroAssembler::ReadCurrentPositionFromRegister(
+ intptr_t register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_CP_TO_REGISTER, register_index);
+}
+
+
+void BytecodeRegExpMacroAssembler::WriteStackPointerToRegister(
+ intptr_t register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER_TO_SP, register_index);
+}
+
+
+void BytecodeRegExpMacroAssembler::ReadStackPointerFromRegister(
+ intptr_t register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_SP_TO_REGISTER, register_index);
+}
+
+
+void BytecodeRegExpMacroAssembler::SetCurrentPositionFromEnd(intptr_t by) {
+ ASSERT(Utils::IsUint(24, by));
+ Emit(BC_SET_CURRENT_POSITION_FROM_END, by);
+}
+
+
+void BytecodeRegExpMacroAssembler::SetRegister(intptr_t register_index,
+ intptr_t to) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER, register_index);
+ Emit32(to);
+}
+
+
+void BytecodeRegExpMacroAssembler::AdvanceRegister(intptr_t register_index,
+ intptr_t by) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_ADVANCE_REGISTER, register_index);
+ Emit32(by);
+}
+
+
+void BytecodeRegExpMacroAssembler::PopCurrentPosition() {
+ Emit(BC_POP_CP, 0);
+}
+
+
+void BytecodeRegExpMacroAssembler::PushCurrentPosition() {
+ Emit(BC_PUSH_CP, 0);
+}
+
+
+void BytecodeRegExpMacroAssembler::Backtrack() {
+ Emit(BC_POP_BT, 0);
+}
+
+
+void BytecodeRegExpMacroAssembler::GoTo(BlockLabel* l) {
+ if (advance_current_end_ == pc_) {
+ // Combine advance current and goto.
+ pc_ = advance_current_start_;
+ Emit(BC_ADVANCE_CP_AND_GOTO, advance_current_offset_);
+ EmitOrLink(l);
+ advance_current_end_ = kInvalidPC;
+ } else {
+ // Regular goto.
+ Emit(BC_GOTO, 0);
+ EmitOrLink(l);
+ }
+}
+
+
+void BytecodeRegExpMacroAssembler::PushBacktrack(BlockLabel* l) {
+ Emit(BC_PUSH_BT, 0);
+ EmitOrLink(l);
+}
+
+
+bool BytecodeRegExpMacroAssembler::Succeed() {
+ Emit(BC_SUCCEED, 0);
+ return false; // Restart matching for global regexp not supported.
+}
+
+
+void BytecodeRegExpMacroAssembler::Fail() {
+ Emit(BC_FAIL, 0);
+}
+
+
+void BytecodeRegExpMacroAssembler::AdvanceCurrentPosition(intptr_t by) {
+ ASSERT(by >= kMinCPOffset);
+ ASSERT(by <= kMaxCPOffset);
+ advance_current_start_ = pc_;
+ advance_current_offset_ = by;
+ Emit(BC_ADVANCE_CP, by);
+ advance_current_end_ = pc_;
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckGreedyLoop(
+ BlockLabel* on_tos_equals_current_position) {
+ Emit(BC_CHECK_GREEDY, 0);
+ EmitOrLink(on_tos_equals_current_position);
+}
+
+
+void BytecodeRegExpMacroAssembler::LoadCurrentCharacter(intptr_t cp_offset,
+ BlockLabel* on_failure,
+ bool check_bounds,
+ intptr_t characters) {
+ ASSERT(cp_offset >= kMinCPOffset);
+ ASSERT(cp_offset <= kMaxCPOffset);
+ int bytecode;
+ if (check_bounds) {
+ if (characters == 4) {
+ bytecode = BC_LOAD_4_CURRENT_CHARS;
+ } else if (characters == 2) {
+ bytecode = BC_LOAD_2_CURRENT_CHARS;
+ } else {
+ ASSERT(characters == 1);
+ bytecode = BC_LOAD_CURRENT_CHAR;
+ }
+ } else {
+ if (characters == 4) {
+ bytecode = BC_LOAD_4_CURRENT_CHARS_UNCHECKED;
+ } else if (characters == 2) {
+ bytecode = BC_LOAD_2_CURRENT_CHARS_UNCHECKED;
+ } else {
+ ASSERT(characters == 1);
+ bytecode = BC_LOAD_CURRENT_CHAR_UNCHECKED;
+ }
+ }
+ Emit(bytecode, cp_offset);
+ if (check_bounds) EmitOrLink(on_failure);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckCharacterLT(uint16_t limit,
+ BlockLabel* on_less) {
+ Emit(BC_CHECK_LT, limit);
+ EmitOrLink(on_less);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckCharacterGT(uint16_t limit,
+ BlockLabel* on_greater) {
+ Emit(BC_CHECK_GT, limit);
+ EmitOrLink(on_greater);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckCharacter(uint32_t c,
+ BlockLabel* on_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_CHECK_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_CHECK_CHAR, c);
+ }
+ EmitOrLink(on_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckAtStart(BlockLabel* on_at_start) {
+ Emit(BC_CHECK_AT_START, 0);
+ EmitOrLink(on_at_start);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckNotAtStart(
+ BlockLabel* on_not_at_start) {
+ Emit(BC_CHECK_NOT_AT_START, 0);
+ EmitOrLink(on_not_at_start);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckNotCharacter(uint32_t c,
+ BlockLabel* on_not_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_CHECK_NOT_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_CHECK_NOT_CHAR, c);
+ }
+ EmitOrLink(on_not_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckCharacterAfterAnd(
+ uint32_t c,
+ uint32_t mask,
+ BlockLabel* on_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_AND_CHECK_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_AND_CHECK_CHAR, c);
+ }
+ Emit32(mask);
+ EmitOrLink(on_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckNotCharacterAfterAnd(
+ uint32_t c,
+ uint32_t mask,
+ BlockLabel* on_not_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_AND_CHECK_NOT_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_AND_CHECK_NOT_CHAR, c);
+ }
+ Emit32(mask);
+ EmitOrLink(on_not_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckNotCharacterAfterMinusAnd(
+ uint16_t c,
+ uint16_t minus,
+ uint16_t mask,
+ BlockLabel* on_not_equal) {
+ Emit(BC_MINUS_AND_CHECK_NOT_CHAR, c);
+ Emit16(minus);
+ Emit16(mask);
+ EmitOrLink(on_not_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckCharacterInRange(
+ uint16_t from,
+ uint16_t to,
+ BlockLabel* on_in_range) {
+ Emit(BC_CHECK_CHAR_IN_RANGE, 0);
+ Emit16(from);
+ Emit16(to);
+ EmitOrLink(on_in_range);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckCharacterNotInRange(
+ uint16_t from,
+ uint16_t to,
+ BlockLabel* on_not_in_range) {
+ Emit(BC_CHECK_CHAR_NOT_IN_RANGE, 0);
+ Emit16(from);
+ Emit16(to);
+ EmitOrLink(on_not_in_range);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckBitInTable(
+ const TypedData& table, BlockLabel* on_bit_set) {
+ Emit(BC_CHECK_BIT_IN_TABLE, 0);
+ EmitOrLink(on_bit_set);
+ for (int i = 0; i < kTableSize; i += kBitsPerByte) {
+ int byte = 0;
+ for (int j = 0; j < kBitsPerByte; j++) {
+ if (table.GetUint8(i + j) != 0) byte |= 1 << j;
+ }
+ Emit8(byte);
+ }
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckNotBackReference(
+ intptr_t start_reg,
+ BlockLabel* on_not_equal) {
+ ASSERT(start_reg >= 0);
+ ASSERT(start_reg <= kMaxRegister);
+ Emit(BC_CHECK_NOT_BACK_REF, start_reg);
+ EmitOrLink(on_not_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(
+ intptr_t start_reg,
+ BlockLabel* on_not_equal) {
+ ASSERT(start_reg >= 0);
+ ASSERT(start_reg <= kMaxRegister);
+ Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
+ EmitOrLink(on_not_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::IfRegisterLT(intptr_t register_index,
+ intptr_t comparand,
+ BlockLabel* on_less_than) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_LT, register_index);
+ Emit32(comparand);
+ EmitOrLink(on_less_than);
+}
+
+
+void BytecodeRegExpMacroAssembler::IfRegisterGE(
+ intptr_t register_index,
+ intptr_t comparand,
+ BlockLabel* on_greater_or_equal) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_GE, register_index);
+ Emit32(comparand);
+ EmitOrLink(on_greater_or_equal);
+}
+
+
+void BytecodeRegExpMacroAssembler::IfRegisterEqPos(intptr_t register_index,
+ BlockLabel* on_eq) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_EQ_POS, register_index);
+ EmitOrLink(on_eq);
+}
+
+
+RawTypedData* BytecodeRegExpMacroAssembler::GetBytecode() {
+ BindBlock(&backtrack_);
+ Emit(BC_POP_BT, 0);
+
+ intptr_t len = length();
+ const TypedData& bytecode =
+ TypedData::Handle(TypedData::New(kTypedDataUint8ArrayCid, len));
+
+ NoSafepointScope no_safepoint;
+ memmove(bytecode.DataAddr(0), buffer_->data(), len);
+
+ return bytecode.raw();
+}
+
+
+intptr_t BytecodeRegExpMacroAssembler::length() {
+ return pc_;
+}
+
+
+void BytecodeRegExpMacroAssembler::Expand() {
+ // BOGUS
+ buffer_->Add(0);
+ buffer_->Add(0);
+ buffer_->Add(0);
+ buffer_->Add(0);
+ intptr_t x = buffer_->length();
+ for (intptr_t i = 0; i < x; i++) buffer_->Add(0);
+}
+
+
+static intptr_t Prepare(const JSRegExp& regexp,
+ const String& subject,
+ Zone* zone) {
+ bool is_one_byte = subject.IsOneByteString() ||
+ subject.IsExternalOneByteString();
+
+ if (regexp.bytecode(is_one_byte) == TypedData::null()) {
+ const String& pattern = String::Handle(zone, regexp.pattern());
+
+ const bool multiline = regexp.is_multi_line();
+ RegExpCompileData* compile_data = new(zone) RegExpCompileData();
+ if (!RegExpParser::ParseRegExp(pattern, multiline, compile_data)) {
+ // Parsing failures are handled in the JSRegExp factory constructor.
+ UNREACHABLE();
+ }
+
+ regexp.set_num_bracket_expressions(compile_data->capture_count);
+ if (compile_data->simple) {
+ regexp.set_is_simple();
+ } else {
+ regexp.set_is_complex();
+ }
+
+ RegExpEngine::CompilationResult result =
+ RegExpEngine::CompileBytecode(compile_data, regexp, is_one_byte, zone);
+ ASSERT(result.bytecode != NULL);
+ ASSERT((regexp.num_registers() == -1) ||
+ (regexp.num_registers() == result.num_registers));
+ regexp.set_num_registers(result.num_registers);
+ regexp.set_bytecode(is_one_byte, *(result.bytecode));
+ }
+
+ ASSERT(regexp.num_registers() != -1);
+
+ return regexp.num_registers() +
+ (Smi::Value(regexp.num_bracket_expressions()) + 1) * 2;
+}
+
+
+static IrregexpInterpreter::IrregexpResult ExecRaw(const JSRegExp& regexp,
+ const String& subject,
+ intptr_t index,
+ int32_t* output,
+ intptr_t output_size,
+ Zone* zone) {
+ bool is_one_byte = subject.IsOneByteString() ||
+ subject.IsExternalOneByteString();
+
+ ASSERT(regexp.num_bracket_expressions() != Smi::null());
+
+ // We must have done EnsureCompiledIrregexp, so we can get the number of
+ // registers.
+ int number_of_capture_registers =
+ (Smi::Value(regexp.num_bracket_expressions()) + 1) * 2;
+ int32_t* raw_output = &output[number_of_capture_registers];
+
+ // We do not touch the actual capture result registers until we know there
+ // has been a match so that we can use those capture results to set the
+ // last match info.
+ for (int i = number_of_capture_registers - 1; i >= 0; i--) {
+ raw_output[i] = -1;
+ }
+
+ const TypedData& bytecode =
+ TypedData::Handle(zone, regexp.bytecode(is_one_byte));
+ ASSERT(!bytecode.IsNull());
+ IrregexpInterpreter::IrregexpResult result =
+ IrregexpInterpreter::Match(bytecode, subject, raw_output, index, zone);
+
+ if (result == IrregexpInterpreter::RE_SUCCESS) {
+ // Copy capture results to the start of the registers array.
+ memmove(output, raw_output, number_of_capture_registers * sizeof(int32_t));
+ }
+ if (result == IrregexpInterpreter::RE_EXCEPTION) {
+ Thread* thread = Thread::Current();
+ Isolate* isolate = thread->isolate();
+ const Instance& exception =
+ Instance::Handle(isolate->object_store()->stack_overflow());
+ Exceptions::Throw(thread, exception);
+ UNREACHABLE();
+ }
+ return result;
+}
+
+
+RawInstance* BytecodeRegExpMacroAssembler::Interpret(const JSRegExp& regexp,
+ const String& subject,
+ const Smi& start_index,
+ Zone* zone) {
+ intptr_t required_registers = Prepare(regexp, subject, zone);
+ if (required_registers < 0) {
+ // Compiling failed with an exception.
+ UNREACHABLE();
+ }
+
+ // V8 uses a shared copy on the isolate when smaller than some threshold.
+ int32_t* output_registers = zone->Alloc<int32_t>(required_registers);
+
+ IrregexpInterpreter::IrregexpResult result = ExecRaw(regexp,
+ subject,
+ start_index.Value(),
+ output_registers,
+ required_registers,
+ zone);
+
+ if (result == IrregexpInterpreter::RE_SUCCESS) {
+ intptr_t capture_count = Smi::Value(regexp.num_bracket_expressions());
+ intptr_t capture_register_count = (capture_count + 1) * 2;
+ ASSERT(required_registers >= capture_register_count);
+
+ const TypedData& result =
+ TypedData::Handle(TypedData::New(kTypedDataInt32ArrayCid,
+ capture_register_count));
+ {
+#ifdef DEBUG
+ // These indices will be used with substring operations that don't check
+ // bounds, so sanity check them here.
+ for (intptr_t i = 0; i < capture_register_count; i++) {
+ int32_t val = output_registers[i];
+ ASSERT(val == -1 || (val >= 0 && val <= subject.Length()));
+ }
+#endif
+
+ NoSafepointScope no_safepoint;
+ memmove(result.DataAddr(0),
+ output_registers,
+ capture_register_count * sizeof(int32_t));
+ }
+
+ return result.raw();
+ }
+ if (result == IrregexpInterpreter::RE_EXCEPTION) {
+ UNREACHABLE();
+ }
+ ASSERT(result == IrregexpInterpreter::RE_FAILURE);
+ return Instance::null();
+}
+
+
+} // namespace dart
diff --git a/runtime/vm/regexp_assembler_bytecode.h b/runtime/vm/regexp_assembler_bytecode.h
new file mode 100644
index 0000000..2de00d0
--- /dev/null
+++ b/runtime/vm/regexp_assembler_bytecode.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2015, 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 VM_REGEXP_ASSEMBLER_BYTECODE_H_
+#define VM_REGEXP_ASSEMBLER_BYTECODE_H_
+
+#include "vm/object.h"
+#include "vm/regexp_assembler.h"
+
+namespace dart {
+
+class BytecodeRegExpMacroAssembler: public RegExpMacroAssembler {
+ public:
+ // Create an assembler. Instructions and relocation information are emitted
+ // into a buffer, with the instructions starting from the beginning and the
+ // relocation information starting from the end of the buffer. See CodeDesc
+ // for a detailed comment on the layout (globals.h).
+ //
+ // If the provided buffer is NULL, the assembler allocates and grows its own
+ // buffer, and buffer_size determines the initial buffer size. The buffer is
+ // owned by the assembler and deallocated upon destruction of the assembler.
+ //
+ // If the provided buffer is not NULL, the assembler uses the provided buffer
+ // for code generation and assumes its size to be buffer_size. If the buffer
+ // is too small, a fatal error occurs. No deallocation of the buffer is done
+ // upon destruction of the assembler.
+ BytecodeRegExpMacroAssembler(ZoneGrowableArray<uint8_t>* buffer,
+ Zone* zone);
+ virtual ~BytecodeRegExpMacroAssembler();
+
+ // The byte-code interpreter checks on each push anyway.
+ virtual intptr_t stack_limit_slack() { return 1; }
+ virtual bool CanReadUnaligned() { return false; }
+ virtual void BindBlock(BlockLabel* label);
+ virtual void AdvanceCurrentPosition(intptr_t by); // Signed cp change.
+ virtual void PopCurrentPosition();
+ virtual void PushCurrentPosition();
+ virtual void Backtrack();
+ virtual void GoTo(BlockLabel* label);
+ virtual void PushBacktrack(BlockLabel* label);
+ virtual bool Succeed();
+ virtual void Fail();
+ virtual void PopRegister(intptr_t register_index);
+ virtual void PushRegister(intptr_t register_index);
+ virtual void AdvanceRegister(intptr_t reg, intptr_t by); // r[reg] += by.
+ virtual void SetCurrentPositionFromEnd(intptr_t by);
+ virtual void SetRegister(intptr_t register_index, intptr_t to);
+ virtual void WriteCurrentPositionToRegister(intptr_t reg, intptr_t cp_offset);
+ virtual void ClearRegisters(intptr_t reg_from, intptr_t reg_to);
+ virtual void ReadCurrentPositionFromRegister(intptr_t reg);
+ virtual void WriteStackPointerToRegister(intptr_t reg);
+ virtual void ReadStackPointerFromRegister(intptr_t reg);
+ virtual void LoadCurrentCharacter(intptr_t cp_offset,
+ BlockLabel* on_end_of_input,
+ bool check_bounds = true,
+ intptr_t characters = 1);
+ virtual void CheckCharacter(unsigned c, BlockLabel* on_equal);
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned mask,
+ BlockLabel* on_equal);
+ virtual void CheckCharacterGT(uint16_t limit, BlockLabel* on_greater);
+ virtual void CheckCharacterLT(uint16_t limit, BlockLabel* on_less);
+ virtual void CheckGreedyLoop(BlockLabel* on_tos_equals_current_position);
+ virtual void CheckAtStart(BlockLabel* on_at_start);
+ virtual void CheckNotAtStart(BlockLabel* on_not_at_start);
+ virtual void CheckNotCharacter(unsigned c, BlockLabel* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned mask,
+ BlockLabel* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uint16_t c,
+ uint16_t minus,
+ uint16_t mask,
+ BlockLabel* on_not_equal);
+ virtual void CheckCharacterInRange(uint16_t from,
+ uint16_t to,
+ BlockLabel* on_in_range);
+ virtual void CheckCharacterNotInRange(uint16_t from,
+ uint16_t to,
+ BlockLabel* on_not_in_range);
+ virtual void CheckBitInTable(const TypedData& table, BlockLabel* on_bit_set);
+ virtual void CheckNotBackReference(intptr_t start_reg,
+ BlockLabel* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(intptr_t start_reg,
+ BlockLabel* on_no_match);
+ virtual void IfRegisterLT(intptr_t register_index,
+ intptr_t comparand,
+ BlockLabel* if_lt);
+ virtual void IfRegisterGE(intptr_t register_index,
+ intptr_t comparand,
+ BlockLabel* if_ge);
+ virtual void IfRegisterEqPos(intptr_t register_index, BlockLabel* if_eq);
+
+ virtual IrregexpImplementation Implementation();
+ // virtual Handle<HeapObject> GetCode(Handle<String> source);
+ RawTypedData* GetBytecode();
+
+ // New
+ virtual bool IsClosed() const {
+ // Added by Dart for the IR version. Bytecode version should never need an
+ // extra goto.
+ return true;
+ }
+ virtual void Print(const char* str) { UNIMPLEMENTED(); }
+ virtual void PrintBlocks() { UNIMPLEMENTED(); }
+ /////
+
+ static RawInstance* Interpret(const JSRegExp& regexp,
+ const String& str,
+ const Smi& start_index,
+ Zone* zone);
+
+ private:
+ void Expand();
+ // Code and bitmap emission.
+ inline void EmitOrLink(BlockLabel* label);
+ inline void Emit32(uint32_t x);
+ inline void Emit16(uint32_t x);
+ inline void Emit8(uint32_t x);
+ inline void Emit(uint32_t bc, uint32_t arg);
+ // Bytecode buffer.
+ intptr_t length();
+
+ // The buffer into which code and relocation info are generated.
+ ZoneGrowableArray<uint8_t>* buffer_;
+
+ // The program counter.
+ intptr_t pc_;
+
+ BlockLabel backtrack_;
+
+ intptr_t advance_current_start_;
+ intptr_t advance_current_offset_;
+ intptr_t advance_current_end_;
+
+ static const int kInvalidPC = -1;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BytecodeRegExpMacroAssembler);
+};
+
+
+} // namespace dart
+
+#endif // VM_REGEXP_ASSEMBLER_BYTECODE_H_
diff --git a/runtime/vm/regexp_assembler_bytecode_inl.h b/runtime/vm/regexp_assembler_bytecode_inl.h
new file mode 100644
index 0000000..2b370db
--- /dev/null
+++ b/runtime/vm/regexp_assembler_bytecode_inl.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2015, 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.
+
+// A light-weight assembler for the Irregexp byte code.
+
+#include "vm/regexp_bytecodes.h"
+
+#ifndef VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+#define VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+
+namespace dart {
+
+void BytecodeRegExpMacroAssembler::Emit(uint32_t byte,
+ uint32_t twenty_four_bits) {
+ uint32_t word = ((twenty_four_bits << BYTECODE_SHIFT) | byte);
+ ASSERT(pc_ <= buffer_->length());
+ if (pc_ + 3 >= buffer_->length()) {
+ Expand();
+ }
+ *reinterpret_cast<uint32_t*>(buffer_->data() + pc_) = word;
+ pc_ += 4;
+}
+
+
+void BytecodeRegExpMacroAssembler::Emit16(uint32_t word) {
+ ASSERT(pc_ <= buffer_->length());
+ if (pc_ + 1 >= buffer_->length()) {
+ Expand();
+ }
+ *reinterpret_cast<uint16_t*>(buffer_->data() + pc_) = word;
+ pc_ += 2;
+}
+
+
+void BytecodeRegExpMacroAssembler::Emit8(uint32_t word) {
+ ASSERT(pc_ <= buffer_->length());
+ if (pc_ == buffer_->length()) {
+ Expand();
+ }
+ *reinterpret_cast<unsigned char*>(buffer_->data() + pc_) = word;
+ pc_ += 1;
+}
+
+
+void BytecodeRegExpMacroAssembler::Emit32(uint32_t word) {
+ ASSERT(pc_ <= buffer_->length());
+ if (pc_ + 3 >= buffer_->length()) {
+ Expand();
+ }
+ *reinterpret_cast<uint32_t*>(buffer_->data() + pc_) = word;
+ pc_ += 4;
+}
+
+} // namespace dart
+
+#endif // VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp_assembler_ir.cc
new file mode 100644
index 0000000..6851926
--- /dev/null
+++ b/runtime/vm/regexp_assembler_ir.cc
@@ -0,0 +1,1923 @@
+// Copyright (c) 2014, 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/regexp_assembler_ir.h"
+
+#include "vm/bit_vector.h"
+#include "vm/compiler.h"
+#include "vm/dart_entry.h"
+#include "vm/flow_graph_builder.h"
+#include "vm/il_printer.h"
+#include "vm/object_store.h"
+#include "vm/regexp.h"
+#include "vm/resolver.h"
+#include "vm/stack_frame.h"
+#include "vm/unibrow-inl.h"
+#include "vm/unicode.h"
+
+#define Z zone()
+
+// Debugging output macros. TAG() is called at the head of each interesting
+// function and prints its name during execution if irregexp tracing is enabled.
+#define TAG() if (FLAG_trace_irregexp) { TAG_(); }
+#define TAG_() \
+ Print(PushArgument( \
+ Bind(new(Z) ConstantInstr(String::ZoneHandle(Z, String::Concat( \
+ String::Handle(String::New("TAG: ")), \
+ String::Handle(String::New(__FUNCTION__)), Heap::kOld))))));
+
+#define PRINT(arg) if (FLAG_trace_irregexp) { Print(arg); }
+
+namespace dart {
+
+DEFINE_FLAG(bool, trace_irregexp, false, "Trace irregexps");
+
+
+static const intptr_t kInvalidTryIndex = CatchClauseNode::kInvalidTryIndex;
+static const intptr_t kNoSourcePos = Scanner::kNoSourcePos;
+static const intptr_t kMinStackSize = 512;
+
+
+void PrintUtf16(uint16_t c) {
+ const char* format = (0x20 <= c && c <= 0x7F) ?
+ "%c" : (c <= 0xff) ? "\\x%02x" : "\\u%04x";
+ OS::Print(format, c);
+}
+
+
+/*
+ * This assembler uses the following main local variables:
+ * - stack_: A pointer to a growable list which we use as an all-purpose stack
+ * storing backtracking offsets, positions & stored register values.
+ * - current_character_: Stores the currently loaded characters (possibly more
+ * than one).
+ * - current_position_: The current position within the string, stored as a
+ * negative offset from the end of the string (i.e. the
+ * position corresponding to str[0] is -str.length).
+ * Note that current_position_ is *not* byte-based, unlike
+ * original V8 code.
+ *
+ * Results are returned though an array of capture indices, stored at
+ * matches_param_. A null array specifies a failure to match. The match indices
+ * [start_inclusive, end_exclusive] for capture group i are stored at positions
+ * matches_param_[i * 2] and matches_param_[i * 2 + 1], respectively. Match
+ * indices of -1 denote non-matched groups. Note that we store these indices
+ * as a negative offset from the end of the string in registers_array_
+ * during processing, and convert them to standard indexes when copying them
+ * to matches_param_ on successful match.
+ */
+IRRegExpMacroAssembler::IRRegExpMacroAssembler(
+ intptr_t specialization_cid,
+ intptr_t capture_count,
+ const ParsedFunction* parsed_function,
+ const ZoneGrowableArray<const ICData*>& ic_data_array,
+ Zone* zone)
+ : RegExpMacroAssembler(zone),
+ specialization_cid_(specialization_cid),
+ parsed_function_(parsed_function),
+ ic_data_array_(ic_data_array),
+ current_instruction_(NULL),
+ stack_(NULL),
+ stack_pointer_(NULL),
+ current_character_(NULL),
+ current_position_(NULL),
+ string_param_(NULL),
+ string_param_length_(NULL),
+ start_index_param_(NULL),
+ registers_count_(0),
+ saved_registers_count_((capture_count + 1) * 2),
+ stack_array_cell_(Array::ZoneHandle(zone, Array::New(1, Heap::kOld))),
+ // The registers array is allocated at a fixed size after assembly.
+ registers_array_(TypedData::ZoneHandle(zone, TypedData::null())) {
+ switch (specialization_cid) {
+ case kOneByteStringCid:
+ case kExternalOneByteStringCid: mode_ = ASCII; break;
+ case kTwoByteStringCid:
+ case kExternalTwoByteStringCid: mode_ = UC16; break;
+ default: UNREACHABLE();
+ }
+
+ InitializeLocals();
+
+ // Allocate an initial stack backing of the minimum stack size. The stack
+ // backing is indirectly referred to so we can reuse it on subsequent matches
+ // even in the case where the backing has been enlarged and thus reallocated.
+ stack_array_cell_.SetAt(0, TypedData::Handle(zone,
+ TypedData::New(kTypedDataInt32ArrayCid, kMinStackSize / 4, Heap::kOld)));
+
+ // Create and generate all preset blocks.
+ entry_block_ =
+ new(zone) GraphEntryInstr(
+ *parsed_function_,
+ new(zone) TargetEntryInstr(block_id_.Alloc(), kInvalidTryIndex),
+ Isolate::kNoDeoptId);
+ start_block_ =
+ new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
+ success_block_ =
+ new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
+ backtrack_block_ =
+ new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
+ exit_block_ =
+ new(zone) JoinEntryInstr(block_id_.Alloc(), kInvalidTryIndex);
+
+ GenerateEntryBlock();
+ GenerateSuccessBlock();
+ GenerateExitBlock();
+
+ blocks_.Add(entry_block_);
+ blocks_.Add(entry_block_->normal_entry());
+ blocks_.Add(start_block_);
+ blocks_.Add(success_block_);
+ blocks_.Add(backtrack_block_);
+ blocks_.Add(exit_block_);
+
+ // Begin emission at the start_block_.
+ set_current_instruction(start_block_);
+}
+
+
+IRRegExpMacroAssembler::~IRRegExpMacroAssembler() { }
+
+
+void IRRegExpMacroAssembler::InitializeLocals() {
+ // All generated functions are expected to have a current-context variable.
+ // This variable is unused in irregexp functions.
+ parsed_function_->current_context_var()->set_index(GetNextLocalIndex());
+
+ // Create local variables and parameters.
+ stack_ = Local(Symbols::stack());
+ stack_pointer_ = Local(Symbols::stack_pointer());
+ registers_ = Local(Symbols::position_registers());
+ current_character_ = Local(Symbols::current_character());
+ current_position_ = Local(Symbols::current_position());
+ string_param_length_ = Local(Symbols::string_param_length());
+ capture_length_ = Local(Symbols::capture_length());
+ match_start_index_ = Local(Symbols::match_start_index());
+ capture_start_index_ = Local(Symbols::capture_start_index());
+ match_end_index_ = Local(Symbols::match_end_index());
+ char_in_capture_ = Local(Symbols::char_in_capture());
+ char_in_match_ = Local(Symbols::char_in_match());
+ index_temp_ = Local(Symbols::index_temp());
+ result_ = Local(Symbols::result());
+
+ string_param_ = Parameter(Symbols::string_param(), 0);
+ start_index_param_ = Parameter(Symbols::start_index_param(), 1);
+}
+
+
+void IRRegExpMacroAssembler::GenerateEntryBlock() {
+ set_current_instruction(entry_block_->normal_entry());
+ TAG();
+
+ // Store string.length.
+ PushArgumentInstr* string_push = PushLocal(string_param_);
+
+ StoreLocal(
+ string_param_length_,
+ Bind(InstanceCall(
+ InstanceCallDescriptor(
+ String::ZoneHandle(Field::GetterSymbol(Symbols::Length()))),
+ string_push)));
+
+ // Store (start_index - string.length) as the current position (since it's a
+ // negative offset from the end of the string).
+ PushArgumentInstr* start_index_push = PushLocal(start_index_param_);
+ PushArgumentInstr* length_push = PushLocal(string_param_length_);
+
+ StoreLocal(current_position_, Bind(Sub(start_index_push, length_push)));
+
+ // Generate a local list variable to represent "registers" and
+ // initialize capture registers (others remain garbage).
+ StoreLocal(registers_, Bind(new(Z) ConstantInstr(registers_array_)));
+ ClearRegisters(0, saved_registers_count_ - 1);
+
+ // Generate a local list variable to represent the backtracking stack.
+ PushArgumentInstr* stack_cell_push =
+ PushArgument(Bind(new(Z) ConstantInstr(stack_array_cell_)));
+ StoreLocal(stack_, Bind(InstanceCall(
+ InstanceCallDescriptor::FromToken(Token::kINDEX),
+ stack_cell_push,
+ PushArgument(Bind(Uint64Constant(0))))));
+ StoreLocal(stack_pointer_, Bind(Int64Constant(-1)));
+
+ // Jump to the start block.
+ current_instruction_->Goto(start_block_);
+}
+
+
+void IRRegExpMacroAssembler::GenerateBacktrackBlock() {
+ set_current_instruction(backtrack_block_);
+ TAG();
+ CheckPreemption();
+
+ const intptr_t entries_count = entry_block_->indirect_entries().length();
+
+ TypedData& offsets = TypedData::ZoneHandle(Z,
+ TypedData::New(kTypedDataInt32ArrayCid, entries_count, Heap::kOld));
+
+ PushArgumentInstr* block_offsets_push =
+ PushArgument(Bind(new(Z) ConstantInstr(offsets)));
+ PushArgumentInstr* block_id_push = PushArgument(Bind(PopStack()));
+
+ Value* offset_value =
+ Bind(InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
+ block_offsets_push,
+ block_id_push));
+
+ backtrack_goto_ = new(Z) IndirectGotoInstr(&offsets, offset_value);
+ CloseBlockWith(backtrack_goto_);
+
+ // Add an edge from the "indirect" goto to each of the targets.
+ for (intptr_t j = 0; j < entries_count; j++) {
+ backtrack_goto_->AddSuccessor(
+ TargetWithJoinGoto(entry_block_->indirect_entries().At(j)));
+ }
+}
+
+
+void IRRegExpMacroAssembler::GenerateSuccessBlock() {
+ set_current_instruction(success_block_);
+ TAG();
+
+ Value* type = Bind(new(Z) ConstantInstr(
+ TypeArguments::ZoneHandle(Z, TypeArguments::null())));
+ Value* length = Bind(Uint64Constant(saved_registers_count_));
+ Value* array = Bind(new(Z) CreateArrayInstr(kNoSourcePos, type, length));
+ StoreLocal(result_, array);
+
+ // Store captured offsets in the `matches` parameter.
+ for (intptr_t i = 0; i < saved_registers_count_; i++) {
+ PushArgumentInstr* matches_push = PushLocal(result_);
+ PushArgumentInstr* index_push = PushArgument(Bind(Uint64Constant(i)));
+
+ // Convert negative offsets from the end of the string to string indices.
+ // TODO(zerny): use positive offsets from the get-go.
+ PushArgumentInstr* offset_push = PushArgument(LoadRegister(i));
+ PushArgumentInstr* len_push = PushLocal(string_param_length_);
+ PushArgumentInstr* value_push =
+ PushArgument(Bind(Add(offset_push, len_push)));
+
+ Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
+ matches_push,
+ index_push,
+ value_push));
+ }
+
+ // Print the result if tracing.
+ PRINT(PushLocal(result_));
+
+ // Return true on success.
+ AppendInstruction(new(Z) ReturnInstr(kNoSourcePos, Bind(LoadLocal(result_))));
+}
+
+
+void IRRegExpMacroAssembler::GenerateExitBlock() {
+ set_current_instruction(exit_block_);
+ TAG();
+
+ // Return false on failure.
+ AppendInstruction(new(Z) ReturnInstr(kNoSourcePos, Bind(LoadLocal(result_))));
+}
+
+
+void IRRegExpMacroAssembler::FinalizeRegistersArray() {
+ ASSERT(registers_count_ >= saved_registers_count_);
+ registers_array_ =
+ TypedData::New(kTypedDataInt32ArrayCid, registers_count_, Heap::kOld);
+}
+
+
+#if defined(TARGET_ARCH_ARM64) || \
+ defined(TARGET_ARCH_ARM) || \
+ defined(TARGET_ARCH_MIPS)
+// Disabling unaligned accesses forces the regexp engine to load characters one
+// by one instead of up to 4 at once, along with the associated performance hit.
+// TODO(zerny): Be less conservative about disabling unaligned accesses.
+// For instance, ARMv6 supports unaligned accesses. Once it is enabled here,
+// update LoadCodeUnitsInstr methods for the appropriate architectures.
+static const bool kEnableUnalignedAccesses = false;
+#else
+static const bool kEnableUnalignedAccesses = true;
+#endif
+bool IRRegExpMacroAssembler::CanReadUnaligned() {
+ return kEnableUnalignedAccesses && !slow_safe();
+}
+
+
+RawArray* IRRegExpMacroAssembler::Execute(
+ const Function& function,
+ const String& input,
+ const Smi& start_offset,
+ Zone* zone) {
+ // Create the argument list.
+ const Array& args = Array::Handle(Array::New(2));
+ args.SetAt(0, input);
+ args.SetAt(1, start_offset);
+
+ // And finally call the generated code.
+
+ const Object& retval =
+ Object::Handle(zone, DartEntry::InvokeFunction(function, args));
+ if (retval.IsError()) {
+ const Error& error = Error::Cast(retval);
+ OS::Print("%s\n", error.ToErrorCString());
+ // Should never happen.
+ UNREACHABLE();
+ }
+
+ if (retval.IsNull()) {
+ return Array::null();
+ }
+
+ ASSERT(retval.IsArray());
+ return Array::Cast(retval).raw();
+}
+
+
+RawBool* IRRegExpMacroAssembler::CaseInsensitiveCompareUC16(
+ RawString* str_raw,
+ RawSmi* lhs_index_raw,
+ RawSmi* rhs_index_raw,
+ RawSmi* length_raw) {
+ const String& str = String::Handle(str_raw);
+ const Smi& lhs_index = Smi::Handle(lhs_index_raw);
+ const Smi& rhs_index = Smi::Handle(rhs_index_raw);
+ const Smi& length = Smi::Handle(length_raw);
+
+ // TODO(zerny): Optimize as single instance. V8 has this as an
+ // isolate member.
+ unibrow::Mapping<unibrow::Ecma262Canonicalize> canonicalize;
+
+ for (intptr_t i = 0; i < length.Value(); i++) {
+ int32_t c1 = str.CharAt(lhs_index.Value() + i);
+ int32_t c2 = str.CharAt(rhs_index.Value() + i);
+ if (c1 != c2) {
+ int32_t s1[1] = { c1 };
+ canonicalize.get(c1, '\0', s1);
+ if (s1[0] != c2) {
+ int32_t s2[1] = { c2 };
+ canonicalize.get(c2, '\0', s2);
+ if (s1[0] != s2[0]) {
+ return Bool::False().raw();
+ }
+ }
+ }
+ }
+ return Bool::True().raw();
+}
+
+
+LocalVariable* IRRegExpMacroAssembler::Parameter(const String& name,
+ intptr_t index) const {
+ const Type& local_type = Type::ZoneHandle(Z, Type::DynamicType());
+ LocalVariable* local =
+ new(Z) LocalVariable(kNoSourcePos, name, local_type);
+
+ intptr_t param_frame_index = kParamEndSlotFromFp + kParamCount - index;
+ local->set_index(param_frame_index);
+
+ return local;
+}
+
+
+LocalVariable* IRRegExpMacroAssembler::Local(const String& name) {
+ const Type& local_type = Type::ZoneHandle(Z, Type::DynamicType());
+ LocalVariable* local =
+ new(Z) LocalVariable(kNoSourcePos, name, local_type);
+ local->set_index(GetNextLocalIndex());
+
+ return local;
+}
+
+
+ConstantInstr* IRRegExpMacroAssembler::Int64Constant(int64_t value) const {
+ return new(Z) ConstantInstr(
+ Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld)));
+}
+
+
+ConstantInstr* IRRegExpMacroAssembler::Uint64Constant(uint64_t value) const {
+ return new(Z) ConstantInstr(
+ Integer::ZoneHandle(Z, Integer::NewFromUint64(value, Heap::kOld)));
+}
+
+
+ConstantInstr* IRRegExpMacroAssembler::BoolConstant(bool value) const {
+ return new(Z) ConstantInstr(value ? Bool::True() : Bool::False());
+}
+
+
+ConstantInstr* IRRegExpMacroAssembler::StringConstant(const char* value) const {
+ return new(Z) ConstantInstr(
+ String::ZoneHandle(Z, String::New(value, Heap::kOld)));
+}
+
+
+ConstantInstr* IRRegExpMacroAssembler::WordCharacterMapConstant() const {
+ const Library& lib = Library::Handle(Z, Library::CoreLibrary());
+ const Class& regexp_class = Class::Handle(Z,
+ lib.LookupClassAllowPrivate(Symbols::JSSyntaxRegExp()));
+ const Field& word_character_field = Field::ZoneHandle(Z,
+ regexp_class.LookupStaticField(Symbols::_wordCharacterMap()));
+ ASSERT(!word_character_field.IsNull());
+
+ if (word_character_field.IsUninitialized()) {
+ word_character_field.EvaluateInitializer();
+ }
+ ASSERT(!word_character_field.IsUninitialized());
+
+ return new(Z) ConstantInstr(
+ Instance::ZoneHandle(Z, word_character_field.value()));
+}
+
+
+ComparisonInstr* IRRegExpMacroAssembler::Comparison(
+ ComparisonKind kind, PushArgumentInstr* lhs, PushArgumentInstr* rhs) {
+ Token::Kind strict_comparison = Token::kEQ_STRICT;
+ Token::Kind intermediate_operator = Token::kILLEGAL;
+ switch (kind) {
+ case kEQ:
+ intermediate_operator = Token::kEQ;
+ break;
+ case kNE:
+ intermediate_operator = Token::kEQ;
+ strict_comparison = Token::kNE_STRICT;
+ break;
+ case kLT:
+ intermediate_operator = Token::kLT;
+ break;
+ case kGT:
+ intermediate_operator = Token::kGT;
+ break;
+ case kLTE:
+ intermediate_operator = Token::kLTE;
+ break;
+ case kGTE:
+ intermediate_operator = Token::kGTE;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ ASSERT(intermediate_operator != Token::kILLEGAL);
+
+ Value* lhs_value =
+ Bind(InstanceCall(
+ InstanceCallDescriptor::FromToken(intermediate_operator),
+ lhs,
+ rhs));
+ Value* rhs_value = Bind(BoolConstant(true));
+
+ return new(Z) StrictCompareInstr(
+ kNoSourcePos, strict_comparison, lhs_value, rhs_value, true);
+}
+
+ComparisonInstr* IRRegExpMacroAssembler::Comparison(
+ ComparisonKind kind, Definition* lhs, Definition* rhs) {
+ PushArgumentInstr* lhs_push = PushArgument(Bind(lhs));
+ PushArgumentInstr* rhs_push = PushArgument(Bind(rhs));
+ return Comparison(kind, lhs_push, rhs_push);
+}
+
+
+StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
+ const Function& function) const {
+ ZoneGrowableArray<PushArgumentInstr*>* arguments =
+ new(Z) ZoneGrowableArray<PushArgumentInstr*>(0);
+ return StaticCall(function, arguments);
+}
+
+
+StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
+ const Function& function,
+ PushArgumentInstr* arg1) const {
+ ZoneGrowableArray<PushArgumentInstr*>* arguments =
+ new(Z) ZoneGrowableArray<PushArgumentInstr*>(1);
+ arguments->Add(arg1);
+
+ return StaticCall(function, arguments);
+}
+
+
+StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
+ const Function& function,
+ PushArgumentInstr* arg1,
+ PushArgumentInstr* arg2) const {
+ ZoneGrowableArray<PushArgumentInstr*>* arguments =
+ new(Z) ZoneGrowableArray<PushArgumentInstr*>(2);
+ arguments->Add(arg1);
+ arguments->Add(arg2);
+
+ return StaticCall(function, arguments);
+}
+
+
+StaticCallInstr* IRRegExpMacroAssembler::StaticCall(
+ const Function& function,
+ ZoneGrowableArray<PushArgumentInstr*>* arguments) const {
+ return new(Z) StaticCallInstr(kNoSourcePos,
+ function,
+ Object::null_array(),
+ arguments,
+ ic_data_array_);
+}
+
+
+InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
+ const InstanceCallDescriptor& desc,
+ PushArgumentInstr* arg1) const {
+ ZoneGrowableArray<PushArgumentInstr*>* arguments =
+ new(Z) ZoneGrowableArray<PushArgumentInstr*>(1);
+ arguments->Add(arg1);
+
+ return InstanceCall(desc, arguments);
+}
+
+
+InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
+ const InstanceCallDescriptor& desc,
+ PushArgumentInstr* arg1,
+ PushArgumentInstr* arg2) const {
+ ZoneGrowableArray<PushArgumentInstr*>* arguments =
+ new(Z) ZoneGrowableArray<PushArgumentInstr*>(2);
+ arguments->Add(arg1);
+ arguments->Add(arg2);
+
+ return InstanceCall(desc, arguments);
+}
+
+
+InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
+ const InstanceCallDescriptor& desc,
+ PushArgumentInstr* arg1,
+ PushArgumentInstr* arg2,
+ PushArgumentInstr* arg3) const {
+ ZoneGrowableArray<PushArgumentInstr*>* arguments =
+ new(Z) ZoneGrowableArray<PushArgumentInstr*>(3);
+ arguments->Add(arg1);
+ arguments->Add(arg2);
+ arguments->Add(arg3);
+
+ return InstanceCall(desc, arguments);
+}
+
+
+InstanceCallInstr* IRRegExpMacroAssembler::InstanceCall(
+ const InstanceCallDescriptor& desc,
+ ZoneGrowableArray<PushArgumentInstr*> *arguments) const {
+ return
+ new(Z) InstanceCallInstr(kNoSourcePos,
+ desc.name,
+ desc.token_kind,
+ arguments,
+ Object::null_array(),
+ desc.checked_argument_count,
+ ic_data_array_);
+}
+
+
+LoadLocalInstr* IRRegExpMacroAssembler::LoadLocal(LocalVariable* local) const {
+ return new(Z) LoadLocalInstr(*local);
+}
+
+
+void IRRegExpMacroAssembler::StoreLocal(LocalVariable* local,
+ Value* value) {
+ Do(new(Z) StoreLocalInstr(*local, value));
+}
+
+
+void IRRegExpMacroAssembler::set_current_instruction(Instruction* instruction) {
+ current_instruction_ = instruction;
+}
+
+
+Value* IRRegExpMacroAssembler::Bind(Definition* definition) {
+ AppendInstruction(definition);
+ definition->set_temp_index(temp_id_.Alloc());
+
+ return new(Z) Value(definition);
+}
+
+
+void IRRegExpMacroAssembler::Do(Definition* definition) {
+ AppendInstruction(definition);
+}
+
+
+Value* IRRegExpMacroAssembler::BindLoadLocal(const LocalVariable& local) {
+ if (local.IsConst()) {
+ return Bind(new(Z) ConstantInstr(*local.ConstValue()));
+ }
+ ASSERT(!local.is_captured());
+ return Bind(new(Z) LoadLocalInstr(local));
+}
+
+
+// In some cases, the V8 irregexp engine generates unreachable code by emitting
+// a jmp not followed by a bind. We cannot do the same, since it is impossible
+// to append to a block following a jmp. In such cases, assume that we are doing
+// the correct thing, but output a warning when tracing.
+#define HANDLE_DEAD_CODE_EMISSION() \
+ if (current_instruction_ == NULL) { \
+ if (FLAG_trace_irregexp) { \
+ OS::Print("WARNING: Attempting to append to a closed assembler. " \
+ "This could be either a bug or generation of dead code " \
+ "inherited from V8.\n"); \
+ } \
+ BlockLabel dummy; \
+ BindBlock(&dummy); \
+ }
+
+void IRRegExpMacroAssembler::AppendInstruction(Instruction* instruction) {
+ HANDLE_DEAD_CODE_EMISSION();
+
+ ASSERT(current_instruction_ != NULL);
+ ASSERT(current_instruction_->next() == NULL);
+
+ temp_id_.Dealloc(instruction->InputCount());
+ arg_id_.Dealloc(instruction->ArgumentCount());
+
+ current_instruction_->LinkTo(instruction);
+ set_current_instruction(instruction);
+}
+
+
+void IRRegExpMacroAssembler::CloseBlockWith(Instruction* instruction) {
+ HANDLE_DEAD_CODE_EMISSION();
+
+ ASSERT(current_instruction_ != NULL);
+ ASSERT(current_instruction_->next() == NULL);
+
+ temp_id_.Dealloc(instruction->InputCount());
+ arg_id_.Dealloc(instruction->ArgumentCount());
+
+ current_instruction_->LinkTo(instruction);
+ set_current_instruction(NULL);
+}
+
+
+void IRRegExpMacroAssembler::GoTo(BlockLabel* to) {
+ if (to == NULL) {
+ Backtrack();
+ } else {
+ to->SetLinked();
+ GoTo(to->block());
+ }
+}
+
+
+// Closes the current block with a goto, and unsets current_instruction_.
+// BindBlock() must be called before emission can continue.
+void IRRegExpMacroAssembler::GoTo(JoinEntryInstr* to) {
+ HANDLE_DEAD_CODE_EMISSION();
+
+ ASSERT(current_instruction_ != NULL);
+ ASSERT(current_instruction_->next() == NULL);
+ current_instruction_->Goto(to);
+ set_current_instruction(NULL);
+}
+
+
+PushArgumentInstr* IRRegExpMacroAssembler::PushArgument(Value* value) {
+ arg_id_.Alloc();
+ PushArgumentInstr* push = new(Z) PushArgumentInstr(value);
+ // Do *not* use Do() for push argument instructions.
+ AppendInstruction(push);
+ return push;
+}
+
+
+PushArgumentInstr* IRRegExpMacroAssembler::PushLocal(LocalVariable* local) {
+ return PushArgument(Bind(LoadLocal(local)));
+}
+
+
+void IRRegExpMacroAssembler::Print(const char* str) {
+ Print(PushArgument(
+ Bind(new(Z) ConstantInstr(
+ String::ZoneHandle(Z, String::New(str, Heap::kOld))))));
+}
+
+
+void IRRegExpMacroAssembler::Print(PushArgumentInstr* argument) {
+ const Library& lib = Library::Handle(Library::CoreLibrary());
+ const Function& print_fn = Function::ZoneHandle(
+ Z, lib.LookupFunctionAllowPrivate(Symbols::print()));
+ Do(StaticCall(print_fn, argument));
+}
+
+
+void IRRegExpMacroAssembler::PrintBlocks() {
+ for (intptr_t i = 0; i < blocks_.length(); i++) {
+ FlowGraphPrinter::PrintBlock(blocks_[i], false);
+ }
+}
+
+
+intptr_t IRRegExpMacroAssembler::stack_limit_slack() {
+ return 32;
+}
+
+
+void IRRegExpMacroAssembler::AdvanceCurrentPosition(intptr_t by) {
+ TAG();
+ if (by != 0) {
+ PushArgumentInstr* cur_pos_push = PushLocal(current_position_);
+ PushArgumentInstr* by_push = PushArgument(Bind(Int64Constant(by)));
+
+ Value* new_pos_value = Bind(Add(cur_pos_push, by_push));
+ StoreLocal(current_position_, new_pos_value);
+ }
+}
+
+
+void IRRegExpMacroAssembler::AdvanceRegister(intptr_t reg, intptr_t by) {
+ TAG();
+ ASSERT(reg >= 0);
+ ASSERT(reg < registers_count_);
+
+ if (by != 0) {
+ PushArgumentInstr* registers_push = PushLocal(registers_);
+ PushArgumentInstr* index_push = PushRegisterIndex(reg);
+ PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
+ PushArgumentInstr* by_push = PushArgument(Bind(Int64Constant(by)));
+ PushArgumentInstr* value_push = PushArgument(Bind(Add(reg_push, by_push)));
+ StoreRegister(registers_push, index_push, value_push);
+ }
+}
+
+
+void IRRegExpMacroAssembler::Backtrack() {
+ TAG();
+ GoTo(backtrack_block_);
+}
+
+
+// A BindBlock is analogous to assigning a label to a basic block.
+// 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->block()->next() == NULL);
+
+ label->SetBound(block_id_.Alloc());
+ blocks_.Add(label->block());
+
+ if (current_instruction_ != NULL) {
+ GoTo(label);
+ }
+ set_current_instruction(label->block());
+
+ // Print the id of the current block if tracing.
+ PRINT(PushArgument(Bind(Uint64Constant(label->block()->block_id()))));
+}
+
+
+intptr_t IRRegExpMacroAssembler::GetNextLocalIndex() {
+ intptr_t id = local_id_.Alloc();
+ return kFirstLocalSlotFromFp - id;
+}
+
+
+Value* IRRegExpMacroAssembler::LoadRegister(intptr_t index) {
+ PushArgumentInstr* registers_push = PushLocal(registers_);
+ PushArgumentInstr* index_push = PushRegisterIndex(index);
+ return Bind(InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
+ registers_push,
+ index_push));
+}
+
+void IRRegExpMacroAssembler::StoreRegister(intptr_t index, intptr_t value) {
+ PushArgumentInstr* registers_push = PushLocal(registers_);
+ PushArgumentInstr* index_push = PushRegisterIndex(index);
+ PushArgumentInstr* value_push = PushArgument(Bind(Uint64Constant(value)));
+ StoreRegister(registers_push, index_push, value_push);
+}
+
+
+void IRRegExpMacroAssembler::StoreRegister(PushArgumentInstr* registers,
+ PushArgumentInstr* index,
+ PushArgumentInstr* value) {
+ TAG();
+ Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
+ registers,
+ index,
+ value));
+}
+
+PushArgumentInstr* IRRegExpMacroAssembler::PushRegisterIndex(intptr_t index) {
+ if (registers_count_ <= index) {
+ registers_count_ = index + 1;
+ }
+ return PushArgument(Bind(Uint64Constant(index)));
+}
+
+
+void IRRegExpMacroAssembler::CheckCharacter(uint32_t c, BlockLabel* on_equal) {
+ TAG();
+ Definition* cur_char_def = LoadLocal(current_character_);
+ Definition* char_def = Uint64Constant(c);
+
+ BranchOrBacktrack(Comparison(kEQ, cur_char_def, char_def), on_equal);
+}
+
+
+void IRRegExpMacroAssembler::CheckCharacterGT(uint16_t limit,
+ BlockLabel* on_greater) {
+ TAG();
+ BranchOrBacktrack(Comparison(kGT,
+ LoadLocal(current_character_),
+ Uint64Constant(limit)),
+ on_greater);
+}
+
+
+void IRRegExpMacroAssembler::CheckAtStart(BlockLabel* on_at_start) {
+ TAG();
+
+ BlockLabel not_at_start;
+
+ // Did we start the match at the start of the string at all?
+ BranchOrBacktrack(Comparison(kNE,
+ LoadLocal(start_index_param_),
+ Uint64Constant(0)),
+ ¬_at_start);
+
+ // If we did, are we still at the start of the input, i.e. is
+ // (offset == string_length * -1)?
+ Definition* neg_len_def =
+ InstanceCall(InstanceCallDescriptor::FromToken(Token::kNEGATE),
+ PushLocal(string_param_length_));
+ Definition* offset_def = LoadLocal(current_position_);
+ BranchOrBacktrack(Comparison(kEQ, neg_len_def, offset_def),
+ on_at_start);
+
+ BindBlock(¬_at_start);
+}
+
+
+void IRRegExpMacroAssembler::CheckNotAtStart(BlockLabel* on_not_at_start) {
+ TAG();
+
+ // Did we start the match at the start of the string at all?
+ BranchOrBacktrack(Comparison(kNE,
+ LoadLocal(start_index_param_),
+ Uint64Constant(0)),
+ on_not_at_start);
+
+ // If we did, are we still at the start of the input, i.e. is
+ // (offset == string_length * -1)?
+ Definition* neg_len_def =
+ InstanceCall(InstanceCallDescriptor::FromToken(Token::kNEGATE),
+ PushLocal(string_param_length_));
+ Definition* offset_def = LoadLocal(current_position_);
+ BranchOrBacktrack(Comparison(kNE, neg_len_def, offset_def),
+ on_not_at_start);
+}
+
+
+void IRRegExpMacroAssembler::CheckCharacterLT(uint16_t limit,
+ BlockLabel* on_less) {
+ TAG();
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(current_character_),
+ Uint64Constant(limit)),
+ on_less);
+}
+
+
+void IRRegExpMacroAssembler::CheckGreedyLoop(BlockLabel* on_equal) {
+ TAG();
+
+ BlockLabel fallthrough;
+
+ Definition* head = PeekStack();
+ Definition* cur_pos_def = LoadLocal(current_position_);
+ BranchOrBacktrack(Comparison(kNE, head, cur_pos_def),
+ &fallthrough);
+
+ // Pop, throwing away the value.
+ Do(PopStack());
+
+ BranchOrBacktrack(NULL, on_equal);
+
+ BindBlock(&fallthrough);
+}
+
+
+void IRRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(
+ intptr_t start_reg,
+ BlockLabel* on_no_match) {
+ TAG();
+ ASSERT(start_reg + 1 <= registers_count_);
+
+ BlockLabel fallthrough;
+
+ PushArgumentInstr* end_push = PushArgument(LoadRegister(start_reg + 1));
+ PushArgumentInstr* start_push = PushArgument(LoadRegister(start_reg));
+ StoreLocal(capture_length_, Bind(Sub(end_push, start_push)));
+
+ // The length of a capture should not be negative. This can only happen
+ // if the end of the capture is unrecorded, or at a point earlier than
+ // the start of the capture.
+ // BranchOrBacktrack(less, on_no_match);
+
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(capture_length_),
+ Uint64Constant(0)),
+ on_no_match);
+
+ // If length is zero, either the capture is empty or it is completely
+ // uncaptured. In either case succeed immediately.
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(capture_length_),
+ Uint64Constant(0)),
+ &fallthrough);
+
+
+ // Check that there are sufficient characters left in the input.
+ PushArgumentInstr* pos_push = PushLocal(current_position_);
+ PushArgumentInstr* len_push = PushLocal(capture_length_);
+ BranchOrBacktrack(
+ Comparison(kGT,
+ InstanceCall(InstanceCallDescriptor::FromToken(Token::kADD),
+ pos_push,
+ len_push),
+ Uint64Constant(0)),
+ on_no_match);
+
+ pos_push = PushLocal(current_position_);
+ len_push = PushLocal(string_param_length_);
+ StoreLocal(match_start_index_, Bind(Add(pos_push, len_push)));
+
+ pos_push = PushArgument(LoadRegister(start_reg));
+ len_push = PushLocal(string_param_length_);
+ StoreLocal(capture_start_index_, Bind(Add(pos_push, len_push)));
+
+ pos_push = PushLocal(match_start_index_);
+ len_push = PushLocal(capture_length_);
+ StoreLocal(match_end_index_, Bind(Add(pos_push, len_push)));
+
+ BlockLabel success;
+ if (mode_ == ASCII) {
+ BlockLabel loop_increment;
+ BlockLabel loop;
+ BindBlock(&loop);
+
+ StoreLocal(char_in_capture_, CharacterAt(capture_start_index_));
+ StoreLocal(char_in_match_, CharacterAt(match_start_index_));
+
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(char_in_capture_),
+ LoadLocal(char_in_match_)),
+ &loop_increment);
+
+ // Mismatch, try case-insensitive match (converting letters to lower-case).
+ PushArgumentInstr* match_char_push = PushLocal(char_in_match_);
+ PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(0x20)));
+ StoreLocal(char_in_match_,
+ Bind(InstanceCall(
+ InstanceCallDescriptor::FromToken(Token::kBIT_OR),
+ match_char_push,
+ mask_push)));
+
+ BlockLabel convert_capture;
+ BlockLabel on_not_in_range;
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(char_in_match_),
+ Uint64Constant('a')),
+ &on_not_in_range);
+ BranchOrBacktrack(Comparison(kGT,
+ LoadLocal(char_in_match_),
+ Uint64Constant('z')),
+ &on_not_in_range);
+ GoTo(&convert_capture);
+ BindBlock(&on_not_in_range);
+
+ // Latin-1: Check for values in range [224,254] but not 247.
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(char_in_match_),
+ Uint64Constant(224)),
+ on_no_match);
+ BranchOrBacktrack(Comparison(kGT,
+ LoadLocal(char_in_match_),
+ Uint64Constant(254)),
+ on_no_match);
+
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(char_in_match_),
+ Uint64Constant(247)),
+ on_no_match);
+
+ // Also convert capture character.
+ BindBlock(&convert_capture);
+
+ PushArgumentInstr* capture_char_push = PushLocal(char_in_capture_);
+ mask_push = PushArgument(Bind(Uint64Constant(0x20)));
+ StoreLocal(char_in_capture_,
+ Bind(InstanceCall(
+ InstanceCallDescriptor::FromToken(Token::kBIT_OR),
+ capture_char_push,
+ mask_push)));
+
+ BranchOrBacktrack(Comparison(kNE,
+ LoadLocal(char_in_match_),
+ LoadLocal(char_in_capture_)),
+ on_no_match);
+
+ BindBlock(&loop_increment);
+
+ // Increment indexes into capture and match strings.
+ PushArgumentInstr* index_push = PushLocal(capture_start_index_);
+ PushArgumentInstr* inc_push = PushArgument(Bind(Uint64Constant(1)));
+ StoreLocal(capture_start_index_, Bind(Add(index_push, inc_push)));
+
+ index_push = PushLocal(match_start_index_);
+ inc_push = PushArgument(Bind(Uint64Constant(1)));
+ StoreLocal(match_start_index_, Bind(Add(index_push, inc_push)));
+
+ // Compare to end of match, and loop if not done.
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(match_start_index_),
+ LoadLocal(match_end_index_)),
+ &loop);
+ } else {
+ ASSERT(mode_ == UC16);
+
+ Value* string_value = Bind(LoadLocal(string_param_));
+ Value* lhs_index_value = Bind(LoadLocal(match_start_index_));
+ Value* rhs_index_value = Bind(LoadLocal(capture_start_index_));
+ Value* length_value = Bind(LoadLocal(capture_length_));
+
+ Definition* is_match_def =
+ new(Z) CaseInsensitiveCompareUC16Instr(
+ string_value,
+ lhs_index_value,
+ rhs_index_value,
+ length_value,
+ specialization_cid_);
+
+ BranchOrBacktrack(Comparison(kNE, is_match_def, BoolConstant(true)),
+ on_no_match);
+ }
+
+ BindBlock(&success);
+
+ // Move current character position to position after match.
+ PushArgumentInstr* match_end_push = PushLocal(match_end_index_);
+ len_push = PushLocal(string_param_length_);
+ StoreLocal(current_position_, Bind(Sub(match_end_push, len_push)));
+
+ BindBlock(&fallthrough);
+}
+
+
+void IRRegExpMacroAssembler::CheckNotBackReference(
+ intptr_t start_reg,
+ BlockLabel* on_no_match) {
+ TAG();
+ ASSERT(start_reg + 1 <= registers_count_);
+
+ BlockLabel fallthrough;
+ BlockLabel success;
+
+ // Find length of back-referenced capture.
+ PushArgumentInstr* end_push = PushArgument(LoadRegister(start_reg + 1));
+ PushArgumentInstr* start_push = PushArgument(LoadRegister(start_reg));
+ StoreLocal(capture_length_, Bind(Sub(end_push, start_push)));
+
+ // Fail on partial or illegal capture (start of capture after end of capture).
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(capture_length_),
+ Uint64Constant(0)),
+ on_no_match);
+
+ // Succeed on empty capture (including no capture)
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(capture_length_),
+ Uint64Constant(0)),
+ &fallthrough);
+
+ // Check that there are sufficient characters left in the input.
+ PushArgumentInstr* pos_push = PushLocal(current_position_);
+ PushArgumentInstr* len_push = PushLocal(capture_length_);
+ BranchOrBacktrack(
+ Comparison(kGT,
+ InstanceCall(InstanceCallDescriptor::FromToken(Token::kADD),
+ pos_push,
+ len_push),
+ Uint64Constant(0)),
+ on_no_match);
+
+ // Compute pointers to match string and capture string.
+ pos_push = PushLocal(current_position_);
+ len_push = PushLocal(string_param_length_);
+ StoreLocal(match_start_index_, Bind(Add(pos_push, len_push)));
+
+ pos_push = PushArgument(LoadRegister(start_reg));
+ len_push = PushLocal(string_param_length_);
+ StoreLocal(capture_start_index_, Bind(Add(pos_push, len_push)));
+
+ pos_push = PushLocal(match_start_index_);
+ len_push = PushLocal(capture_length_);
+ StoreLocal(match_end_index_, Bind(Add(pos_push, len_push)));
+
+ BlockLabel loop;
+ BindBlock(&loop);
+
+ StoreLocal(char_in_capture_, CharacterAt(capture_start_index_));
+ StoreLocal(char_in_match_, CharacterAt(match_start_index_));
+
+ BranchOrBacktrack(Comparison(kNE,
+ LoadLocal(char_in_capture_),
+ LoadLocal(char_in_match_)),
+ on_no_match);
+
+ // Increment indexes into capture and match strings.
+ PushArgumentInstr* index_push = PushLocal(capture_start_index_);
+ PushArgumentInstr* inc_push = PushArgument(Bind(Uint64Constant(1)));
+ StoreLocal(capture_start_index_, Bind(Add(index_push, inc_push)));
+
+ index_push = PushLocal(match_start_index_);
+ inc_push = PushArgument(Bind(Uint64Constant(1)));
+ StoreLocal(match_start_index_, Bind(Add(index_push, inc_push)));
+
+ // Check if we have reached end of match area.
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(match_start_index_),
+ LoadLocal(match_end_index_)),
+ &loop);
+
+ BindBlock(&success);
+
+ // Move current character position to position after match.
+ PushArgumentInstr* match_end_push = PushLocal(match_end_index_);
+ len_push = PushLocal(string_param_length_);
+ StoreLocal(current_position_, Bind(Sub(match_end_push, len_push)));
+
+ BindBlock(&fallthrough);
+}
+
+
+void IRRegExpMacroAssembler::CheckNotCharacter(uint32_t c,
+ BlockLabel* on_not_equal) {
+ TAG();
+ BranchOrBacktrack(Comparison(kNE,
+ LoadLocal(current_character_),
+ Uint64Constant(c)),
+ on_not_equal);
+}
+
+
+void IRRegExpMacroAssembler::CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ BlockLabel* on_equal) {
+ TAG();
+
+ Definition* actual_def = LoadLocal(current_character_);
+ Definition* expected_def = Uint64Constant(c);
+
+ PushArgumentInstr* actual_push = PushArgument(Bind(actual_def));
+ PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(mask)));
+ actual_def = InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
+ actual_push,
+ mask_push);
+
+ BranchOrBacktrack(Comparison(kEQ, actual_def, expected_def), on_equal);
+}
+
+
+void IRRegExpMacroAssembler::CheckNotCharacterAfterAnd(
+ uint32_t c,
+ uint32_t mask,
+ BlockLabel* on_not_equal) {
+ TAG();
+
+ Definition* actual_def = LoadLocal(current_character_);
+ Definition* expected_def = Uint64Constant(c);
+
+ PushArgumentInstr* actual_push = PushArgument(Bind(actual_def));
+ PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(mask)));
+ actual_def = InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
+ actual_push,
+ mask_push);
+
+ BranchOrBacktrack(Comparison(kNE, actual_def, expected_def), on_not_equal);
+}
+
+
+void IRRegExpMacroAssembler::CheckNotCharacterAfterMinusAnd(
+ uint16_t c,
+ uint16_t minus,
+ uint16_t mask,
+ BlockLabel* on_not_equal) {
+ TAG();
+ ASSERT(minus < Utf16::kMaxCodeUnit); // NOLINT
+
+ Definition* actual_def = LoadLocal(current_character_);
+ Definition* expected_def = Uint64Constant(c);
+
+ PushArgumentInstr* actual_push = PushArgument(Bind(actual_def));
+ PushArgumentInstr* minus_push = PushArgument(Bind(Uint64Constant(minus)));
+
+ actual_push = PushArgument(Bind(Sub(actual_push, minus_push)));
+ PushArgumentInstr* mask_push = PushArgument(Bind(Uint64Constant(mask)));
+ actual_def = InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
+ actual_push,
+ mask_push);
+
+ BranchOrBacktrack(Comparison(kNE, actual_def, expected_def), on_not_equal);
+}
+
+
+void IRRegExpMacroAssembler::CheckCharacterInRange(
+ uint16_t from,
+ uint16_t to,
+ BlockLabel* on_in_range) {
+ TAG();
+ ASSERT(from <= to);
+
+ // TODO(zerny): All range comparisons could be done cheaper with unsigned
+ // compares. This pattern repeats in various places.
+
+ BlockLabel on_not_in_range;
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(current_character_),
+ Uint64Constant(from)),
+ &on_not_in_range);
+ BranchOrBacktrack(Comparison(kGT,
+ LoadLocal(current_character_),
+ Uint64Constant(to)),
+ &on_not_in_range);
+ BranchOrBacktrack(NULL, on_in_range);
+
+ BindBlock(&on_not_in_range);
+}
+
+
+void IRRegExpMacroAssembler::CheckCharacterNotInRange(
+ uint16_t from,
+ uint16_t to,
+ BlockLabel* on_not_in_range) {
+ TAG();
+ ASSERT(from <= to);
+
+ BranchOrBacktrack(Comparison(kLT,
+ LoadLocal(current_character_),
+ Uint64Constant(from)),
+ on_not_in_range);
+
+ BranchOrBacktrack(Comparison(kGT,
+ LoadLocal(current_character_),
+ Uint64Constant(to)),
+ on_not_in_range);
+}
+
+
+void IRRegExpMacroAssembler::CheckBitInTable(
+ const TypedData& table,
+ BlockLabel* on_bit_set) {
+ TAG();
+
+ PushArgumentInstr* table_push =
+ PushArgument(Bind(new(Z) ConstantInstr(table)));
+ PushArgumentInstr* index_push = PushLocal(current_character_);
+
+ if (mode_ != ASCII || kTableMask != Symbols::kMaxOneCharCodeSymbol) {
+ PushArgumentInstr* mask_push =
+ PushArgument(Bind(Uint64Constant(kTableSize - 1)));
+ index_push = PushArgument(
+ Bind(InstanceCall(InstanceCallDescriptor::FromToken(Token::kBIT_AND),
+ index_push,
+ mask_push)));
+ }
+
+ Definition* byte_def =
+ InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
+ table_push,
+ index_push);
+ Definition* zero_def = Int64Constant(0);
+
+ BranchOrBacktrack(Comparison(kNE, byte_def, zero_def), on_bit_set);
+}
+
+
+bool IRRegExpMacroAssembler::CheckSpecialCharacterClass(
+ uint16_t type,
+ BlockLabel* on_no_match) {
+ TAG();
+
+ // Range checks (c in min..max) are generally implemented by an unsigned
+ // (c - min) <= (max - min) check
+ switch (type) {
+ case 's':
+ // Match space-characters
+ if (mode_ == ASCII) {
+ // One byte space characters are '\t'..'\r', ' ' and \u00a0.
+ BlockLabel success;
+ // Space (' ').
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant(' ')),
+ &success);
+ // Check range 0x09..0x0d.
+ CheckCharacterInRange('\t', '\r', &success);
+ // \u00a0 (NBSP).
+ BranchOrBacktrack(Comparison(kNE,
+ LoadLocal(current_character_),
+ Uint64Constant(0x00a0)),
+ on_no_match);
+ BindBlock(&success);
+ return true;
+ }
+ return false;
+ case 'S':
+ // The emitted code for generic character classes is good enough.
+ return false;
+ case 'd':
+ // Match ASCII digits ('0'..'9')
+ CheckCharacterNotInRange('0', '9', on_no_match);
+ return true;
+ case 'D':
+ // Match non ASCII-digits
+ CheckCharacterInRange('0', '9', on_no_match);
+ return true;
+ case '.': {
+ // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant('\n')),
+ on_no_match);
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant('\r')),
+ on_no_match);
+ if (mode_ == UC16) {
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant(0x2028)),
+ on_no_match);
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant(0x2029)),
+ on_no_match);
+ }
+ return true;
+ }
+ case 'w': {
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ BranchOrBacktrack(Comparison(kGT,
+ LoadLocal(current_character_),
+ Uint64Constant('z')),
+ on_no_match);
+ }
+
+ PushArgumentInstr* table_push =
+ PushArgument(Bind(WordCharacterMapConstant()));
+ PushArgumentInstr* index_push = PushLocal(current_character_);
+
+ Definition* byte_def =
+ InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
+ table_push,
+ index_push);
+ Definition* zero_def = Int64Constant(0);
+
+ BranchOrBacktrack(Comparison(kEQ, byte_def, zero_def), on_no_match);
+
+ return true;
+ }
+ case 'W': {
+ BlockLabel done;
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ BranchOrBacktrack(Comparison(kGT,
+ LoadLocal(current_character_),
+ Uint64Constant('z')),
+ &done);
+ }
+
+ // TODO(zerny): Refactor to use CheckBitInTable if possible.
+
+ PushArgumentInstr* table_push =
+ PushArgument(Bind(WordCharacterMapConstant()));
+ PushArgumentInstr* index_push = PushLocal(current_character_);
+
+ Definition* byte_def =
+ InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
+ table_push,
+ index_push);
+ Definition* zero_def = Int64Constant(0);
+
+ BranchOrBacktrack(Comparison(kNE, byte_def, zero_def), on_no_match);
+
+ if (mode_ != ASCII) {
+ BindBlock(&done);
+ }
+ return true;
+ }
+ // Non-standard classes (with no syntactic shorthand) used internally.
+ case '*':
+ // Match any character.
+ return true;
+ case 'n': {
+ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029).
+ // The opposite of '.'.
+ BlockLabel success;
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant('\n')),
+ &success);
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant('\r')),
+ &success);
+ if (mode_ == UC16) {
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant(0x2028)),
+ &success);
+ BranchOrBacktrack(Comparison(kEQ,
+ LoadLocal(current_character_),
+ Uint64Constant(0x2029)),
+ &success);
+ }
+ BranchOrBacktrack(NULL, on_no_match);
+ BindBlock(&success);
+ return true;
+ }
+ // No custom implementation (yet): s(uint16_t), S(uint16_t).
+ default:
+ return false;
+ }
+}
+
+
+void IRRegExpMacroAssembler::Fail() {
+ TAG();
+ ASSERT(FAILURE == 0); // Return value for failure is zero.
+ if (!global()) {
+ UNREACHABLE(); // Dart regexps are always global.
+ }
+ GoTo(exit_block_);
+}
+
+
+void IRRegExpMacroAssembler::IfRegisterGE(intptr_t reg,
+ intptr_t comparand,
+ BlockLabel* if_ge) {
+ TAG();
+ PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
+ PushArgumentInstr* pos = PushArgument(Bind(Int64Constant(comparand)));
+ BranchOrBacktrack(Comparison(kGTE, reg_push, pos), if_ge);
+}
+
+
+void IRRegExpMacroAssembler::IfRegisterLT(intptr_t reg,
+ intptr_t comparand,
+ BlockLabel* if_lt) {
+ TAG();
+ PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
+ PushArgumentInstr* pos = PushArgument(Bind(Int64Constant(comparand)));
+ BranchOrBacktrack(Comparison(kLT, reg_push, pos), if_lt);
+}
+
+
+void IRRegExpMacroAssembler::IfRegisterEqPos(intptr_t reg,
+ BlockLabel* if_eq) {
+ TAG();
+ PushArgumentInstr* reg_push = PushArgument(LoadRegister(reg));
+ PushArgumentInstr* pos = PushArgument(Bind(LoadLocal(current_position_)));
+ BranchOrBacktrack(Comparison(kEQ, reg_push, pos), if_eq);
+}
+
+
+RegExpMacroAssembler::IrregexpImplementation
+ IRRegExpMacroAssembler::Implementation() {
+ return kIRImplementation;
+}
+
+
+void IRRegExpMacroAssembler::LoadCurrentCharacter(intptr_t cp_offset,
+ BlockLabel* on_end_of_input,
+ bool check_bounds,
+ intptr_t characters) {
+ TAG();
+ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
+ ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
+ if (check_bounds) {
+ CheckPosition(cp_offset + characters - 1, on_end_of_input);
+ }
+ LoadCurrentCharacterUnchecked(cp_offset, characters);
+}
+
+
+void IRRegExpMacroAssembler::PopCurrentPosition() {
+ TAG();
+ StoreLocal(current_position_, Bind(PopStack()));
+}
+
+
+void IRRegExpMacroAssembler::PopRegister(intptr_t reg) {
+ TAG();
+ ASSERT(reg < registers_count_);
+ PushArgumentInstr* registers_push = PushLocal(registers_);
+ PushArgumentInstr* index_push = PushRegisterIndex(reg);
+ PushArgumentInstr* pop_push = PushArgument(Bind(PopStack()));
+ StoreRegister(registers_push, index_push, pop_push);
+}
+
+
+void IRRegExpMacroAssembler::PushStack(Definition *definition) {
+ PushArgumentInstr* stack_push = PushLocal(stack_);
+ PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
+ StoreLocal(stack_pointer_,
+ Bind(Add(stack_pointer_push,
+ PushArgument(Bind(Uint64Constant(1))))));
+ stack_pointer_push = PushLocal(stack_pointer_);
+ // TODO(zerny): bind value and push could break stack discipline.
+ PushArgumentInstr* value_push = PushArgument(Bind(definition));
+ Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
+ stack_push,
+ stack_pointer_push,
+ value_push));
+}
+
+
+Definition* IRRegExpMacroAssembler::PopStack() {
+ PushArgumentInstr* stack_push = PushLocal(stack_);
+ PushArgumentInstr* stack_pointer_push1 = PushLocal(stack_pointer_);
+ PushArgumentInstr* stack_pointer_push2 = PushLocal(stack_pointer_);
+ StoreLocal(stack_pointer_,
+ Bind(Sub(stack_pointer_push2,
+ PushArgument(Bind(Uint64Constant(1))))));
+ return InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
+ stack_push,
+ stack_pointer_push1);
+}
+
+
+Definition* IRRegExpMacroAssembler::PeekStack() {
+ PushArgumentInstr* stack_push = PushLocal(stack_);
+ PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
+ return InstanceCall(InstanceCallDescriptor::FromToken(Token::kINDEX),
+ stack_push,
+ stack_pointer_push);
+}
+
+
+// Pushes the location corresponding to label to the backtracking stack.
+void IRRegExpMacroAssembler::PushBacktrack(BlockLabel* label) {
+ TAG();
+
+ // Ensure that targets of indirect jumps are never accessed through a
+ // normal control flow instructions by creating a new block for each backtrack
+ // target.
+ IndirectEntryInstr* indirect_target = IndirectWithJoinGoto(label->block());
+
+ // Add a fake edge from the graph entry for data flow analysis.
+ entry_block_->AddIndirectEntry(indirect_target);
+
+ ConstantInstr* offset = Uint64Constant(indirect_target->indirect_id());
+ PushStack(offset);
+ CheckStackLimit();
+}
+
+
+void IRRegExpMacroAssembler::PushCurrentPosition() {
+ TAG();
+ PushStack(LoadLocal(current_position_));
+}
+
+
+void IRRegExpMacroAssembler::PushRegister(intptr_t reg) {
+ TAG();
+ // TODO(zerny): Refactor PushStack so it can be reused here.
+ PushArgumentInstr* stack_push = PushLocal(stack_);
+ PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
+ StoreLocal(stack_pointer_,
+ Bind(Add(stack_pointer_push,
+ PushArgument(Bind(Uint64Constant(1))))));
+ stack_pointer_push = PushLocal(stack_pointer_);
+ // TODO(zerny): bind value and push could break stack discipline.
+ PushArgumentInstr* value_push = PushArgument(LoadRegister(reg));
+ Do(InstanceCall(InstanceCallDescriptor::FromToken(Token::kASSIGN_INDEX),
+ stack_push,
+ stack_pointer_push,
+ value_push));
+ CheckStackLimit();
+}
+
+
+// Checks that (stack.capacity - stack_limit_slack) > stack_pointer.
+// This ensures that up to stack_limit_slack stack pushes can be
+// done without exhausting the stack space. If the check fails the
+// stack will be grown.
+void IRRegExpMacroAssembler::CheckStackLimit() {
+ TAG();
+ PushArgumentInstr* stack_push = PushLocal(stack_);
+ PushArgumentInstr* length_push = PushArgument(Bind(InstanceCall(
+ InstanceCallDescriptor(
+ String::ZoneHandle(Field::GetterSymbol(Symbols::Length()))),
+ stack_push)));
+ PushArgumentInstr* capacity_push = PushArgument(Bind(Sub(
+ length_push,
+ PushArgument(Bind(Uint64Constant(stack_limit_slack()))))));
+ PushArgumentInstr* stack_pointer_push = PushLocal(stack_pointer_);
+ BranchInstr* branch = new(Z) BranchInstr(
+ Comparison(kGT, capacity_push, stack_pointer_push));
+ CloseBlockWith(branch);
+
+ BlockLabel grow_stack;
+ BlockLabel fallthrough;
+ *branch->true_successor_address() =
+ TargetWithJoinGoto(fallthrough.block());
+ *branch->false_successor_address() =
+ TargetWithJoinGoto(grow_stack.block());
+
+ BindBlock(&grow_stack);
+ GrowStack();
+
+ BindBlock(&fallthrough);
+}
+
+
+void IRRegExpMacroAssembler::GrowStack() {
+ TAG();
+ Value* cell = Bind(new(Z) ConstantInstr(stack_array_cell_));
+ StoreLocal(stack_, Bind(new(Z) GrowRegExpStackInstr(cell)));
+}
+
+
+void IRRegExpMacroAssembler::ReadCurrentPositionFromRegister(intptr_t reg) {
+ TAG();
+ StoreLocal(current_position_, LoadRegister(reg));
+}
+
+// Resets the tip of the stack to the value stored in reg.
+void IRRegExpMacroAssembler::ReadStackPointerFromRegister(intptr_t reg) {
+ TAG();
+ ASSERT(reg < registers_count_);
+ StoreLocal(stack_pointer_, LoadRegister(reg));
+}
+
+void IRRegExpMacroAssembler::SetCurrentPositionFromEnd(intptr_t by) {
+ TAG();
+
+ BlockLabel after_position;
+
+ Definition* cur_pos_def = LoadLocal(current_position_);
+ Definition* by_value_def = Int64Constant(-by);
+
+ BranchOrBacktrack(Comparison(kGTE, cur_pos_def, by_value_def),
+ &after_position);
+
+ StoreLocal(current_position_, Bind(Int64Constant(-by)));
+
+ // On RegExp code entry (where this operation is used), the character before
+ // the current position is expected to be already loaded.
+ // We have advanced the position, so it's safe to read backwards.
+ LoadCurrentCharacterUnchecked(-1, 1);
+
+ BindBlock(&after_position);
+}
+
+
+void IRRegExpMacroAssembler::SetRegister(intptr_t reg, intptr_t to) {
+ TAG();
+ // Reserved for positions!
+ ASSERT(reg >= saved_registers_count_);
+ StoreRegister(reg, to);
+}
+
+
+bool IRRegExpMacroAssembler::Succeed() {
+ TAG();
+ GoTo(success_block_);
+ return global();
+}
+
+
+void IRRegExpMacroAssembler::WriteCurrentPositionToRegister(
+ intptr_t reg, intptr_t cp_offset) {
+ TAG();
+
+ PushArgumentInstr* registers_push = PushLocal(registers_);
+ PushArgumentInstr* index_push = PushRegisterIndex(reg);
+ PushArgumentInstr* pos_push = PushLocal(current_position_);
+ PushArgumentInstr* off_push = PushArgument(Bind(Int64Constant(cp_offset)));
+ PushArgumentInstr* neg_off_push = PushArgument(Bind(Add(pos_push, off_push)));
+ // Push the negative offset; these are converted to positive string positions
+ // within the success block.
+ StoreRegister(registers_push, index_push, neg_off_push);
+}
+
+
+void IRRegExpMacroAssembler::ClearRegisters(
+ intptr_t reg_from, intptr_t reg_to) {
+ TAG();
+
+ ASSERT(reg_from <= reg_to);
+
+ // In order to clear registers to a final result value of -1, set them to
+ // (-1 - string length), the offset of -1 from the end of the string.
+
+ for (intptr_t reg = reg_from; reg <= reg_to; reg++) {
+ PushArgumentInstr* registers_push = PushLocal(registers_);
+ PushArgumentInstr* index_push = PushRegisterIndex(reg);
+ PushArgumentInstr* minus_one_push =
+ PushArgument(Bind(Int64Constant(-1)));
+ PushArgumentInstr* length_push = PushLocal(string_param_length_);
+ PushArgumentInstr* value_push =
+ PushArgument(Bind(Sub(minus_one_push, length_push)));
+ StoreRegister(registers_push, index_push, value_push);
+ }
+}
+
+
+void IRRegExpMacroAssembler::WriteStackPointerToRegister(intptr_t reg) {
+ TAG();
+
+ PushArgumentInstr* registers_push = PushLocal(registers_);
+ PushArgumentInstr* index_push = PushRegisterIndex(reg);
+ PushArgumentInstr* tip_push = PushLocal(stack_pointer_);
+ StoreRegister(registers_push, index_push, tip_push);
+}
+
+
+// Private methods:
+
+
+void IRRegExpMacroAssembler::CheckPosition(intptr_t cp_offset,
+ BlockLabel* on_outside_input) {
+ TAG();
+ Definition* curpos_def = LoadLocal(current_position_);
+ Definition* cp_off_def = Int64Constant(-cp_offset);
+
+ // If (current_position_ < -cp_offset), we are in bounds.
+ // Remember, current_position_ is a negative offset from the string end.
+
+ BranchOrBacktrack(Comparison(kGTE, curpos_def, cp_off_def),
+ on_outside_input);
+}
+
+
+void IRRegExpMacroAssembler::BranchOrBacktrack(
+ ComparisonInstr* comparison,
+ BlockLabel* true_successor) {
+ if (comparison == NULL) { // No condition
+ if (true_successor == NULL) {
+ Backtrack();
+ return;
+ }
+ GoTo(true_successor);
+ return;
+ }
+
+ // If no successor block has been passed in, backtrack.
+ JoinEntryInstr* true_successor_block = backtrack_block_;
+ if (true_successor != NULL) {
+ true_successor->SetLinked();
+ true_successor_block = true_successor->block();
+ }
+ ASSERT(true_successor_block != NULL);
+
+ // If the condition is not true, fall through to a new block.
+ BlockLabel fallthrough;
+
+ BranchInstr* branch = new(Z) BranchInstr(comparison);
+ *branch->true_successor_address() =
+ TargetWithJoinGoto(true_successor_block);
+ *branch->false_successor_address() =
+ TargetWithJoinGoto(fallthrough.block());
+
+ CloseBlockWith(branch);
+ BindBlock(&fallthrough);
+}
+
+
+TargetEntryInstr* IRRegExpMacroAssembler::TargetWithJoinGoto(
+ JoinEntryInstr* dst) {
+ TargetEntryInstr* target = new(Z) TargetEntryInstr(
+ block_id_.Alloc(), kInvalidTryIndex);
+ blocks_.Add(target);
+
+ target->AppendInstruction(new(Z) GotoInstr(dst));
+
+ return target;
+}
+
+
+IndirectEntryInstr* IRRegExpMacroAssembler::IndirectWithJoinGoto(
+ JoinEntryInstr* dst) {
+ IndirectEntryInstr* target = new(Z) IndirectEntryInstr(
+ block_id_.Alloc(), indirect_id_.Alloc(), kInvalidTryIndex);
+ blocks_.Add(target);
+
+ target->AppendInstruction(new(Z) GotoInstr(dst));
+
+ return target;
+}
+
+
+void IRRegExpMacroAssembler::CheckPreemption() {
+ TAG();
+ AppendInstruction(new(Z) CheckStackOverflowInstr(kNoSourcePos, 0));
+}
+
+
+Definition* IRRegExpMacroAssembler::Add(
+ PushArgumentInstr* lhs,
+ PushArgumentInstr* rhs) {
+ return InstanceCall(InstanceCallDescriptor::FromToken(Token::kADD), lhs, rhs);
+}
+
+
+Definition* IRRegExpMacroAssembler::Sub(
+ PushArgumentInstr* lhs,
+ PushArgumentInstr* rhs) {
+ return InstanceCall(InstanceCallDescriptor::FromToken(Token::kSUB), lhs, rhs);
+}
+
+
+void IRRegExpMacroAssembler::LoadCurrentCharacterUnchecked(
+ intptr_t cp_offset, intptr_t characters) {
+ TAG();
+
+ ASSERT(characters == 1 || CanReadUnaligned());
+ if (mode_ == ASCII) {
+ ASSERT(characters == 1 || characters == 2 || characters == 4);
+ } else {
+ ASSERT(mode_ == UC16);
+ ASSERT(characters == 1 || characters == 2);
+ }
+
+ // Calculate the addressed string index as:
+ // cp_offset + current_position_ + string_param_length_
+ // TODO(zerny): Avoid generating 'add' instance-calls here.
+ PushArgumentInstr* off_arg =
+ PushArgument(Bind(Int64Constant(cp_offset)));
+ PushArgumentInstr* pos_arg =
+ PushArgument(BindLoadLocal(*current_position_));
+ PushArgumentInstr* off_pos_arg =
+ PushArgument(Bind(Add(off_arg, pos_arg)));
+ PushArgumentInstr* len_arg =
+ PushArgument(BindLoadLocal(*string_param_length_));
+ // Index is stored in a temporary local so that we can later load it safely.
+ StoreLocal(index_temp_, Bind(Add(off_pos_arg, len_arg)));
+
+ // Load and store the code units.
+ Value* code_unit_value = LoadCodeUnitsAt(index_temp_, characters);
+ StoreLocal(current_character_, code_unit_value);
+ PRINT(PushLocal(current_character_));
+}
+
+
+Value* IRRegExpMacroAssembler::CharacterAt(LocalVariable* index) {
+ return LoadCodeUnitsAt(index, 1);
+}
+
+
+Value* IRRegExpMacroAssembler::LoadCodeUnitsAt(LocalVariable* index,
+ intptr_t characters) {
+ // Bind the pattern as the load receiver.
+ Value* pattern_val = BindLoadLocal(*string_param_);
+ if (RawObject::IsExternalStringClassId(specialization_cid_)) {
+ // The data of an external string is stored through two indirections.
+ intptr_t external_offset = 0;
+ intptr_t data_offset = 0;
+ if (specialization_cid_ == kExternalOneByteStringCid) {
+ external_offset = ExternalOneByteString::external_data_offset();
+ data_offset = RawExternalOneByteString::ExternalData::data_offset();
+ } else if (specialization_cid_ == kExternalTwoByteStringCid) {
+ external_offset = ExternalTwoByteString::external_data_offset();
+ data_offset = RawExternalTwoByteString::ExternalData::data_offset();
+ } else {
+ UNREACHABLE();
+ }
+ // This pushes untagged values on the stack which are immediately consumed:
+ // the first value is consumed to obtain the second value which is consumed
+ // by LoadCodeUnitsAtInstr below.
+ Value* external_val =
+ Bind(new(Z) LoadUntaggedInstr(pattern_val, external_offset));
+ pattern_val =
+ Bind(new(Z) LoadUntaggedInstr(external_val, data_offset));
+ }
+
+ // Here pattern_val might be untagged so this must not trigger a GC.
+ Value* index_val = BindLoadLocal(*index);
+
+ return Bind(new(Z) LoadCodeUnitsInstr(
+ pattern_val,
+ index_val,
+ characters,
+ specialization_cid_,
+ Scanner::kNoSourcePos));
+}
+
+
+#undef __
+
+} // namespace dart
diff --git a/runtime/vm/regexp_assembler_ir.h b/runtime/vm/regexp_assembler_ir.h
new file mode 100644
index 0000000..77d09d5
--- /dev/null
+++ b/runtime/vm/regexp_assembler_ir.h
@@ -0,0 +1,443 @@
+// Copyright (c) 2014, 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 VM_REGEXP_ASSEMBLER_IR_H_
+#define VM_REGEXP_ASSEMBLER_IR_H_
+
+#include "vm/assembler.h"
+#include "vm/intermediate_language.h"
+#include "vm/object.h"
+#include "vm/regexp_assembler.h"
+
+namespace dart {
+
+class IRRegExpMacroAssembler : public RegExpMacroAssembler {
+ public:
+ // Type of input string to generate code for.
+ enum Mode { ASCII = 1, UC16 = 2 };
+
+ // Result of calling generated native RegExp code.
+ // RETRY: Something significant changed during execution, and the matching
+ // should be retried from scratch.
+ // EXCEPTION: Something failed during execution. If no exception has been
+ // thrown, it's an internal out-of-memory, and the caller should
+ // throw the exception.
+ // FAILURE: Matching failed.
+ // SUCCESS: Matching succeeded, and the output array has been filled with
+ // capture positions.
+ enum Result { RETRY = -2, EXCEPTION = -1, FAILURE = 0, SUCCESS = 1 };
+
+ IRRegExpMacroAssembler(intptr_t specialization_cid,
+ intptr_t capture_count,
+ const ParsedFunction* parsed_function,
+ const ZoneGrowableArray<const ICData*>& ic_data_array,
+ Zone* zone);
+ virtual ~IRRegExpMacroAssembler();
+
+ virtual bool CanReadUnaligned();
+
+ // Compares two-byte strings case insensitively.
+ // Called from generated RegExp code.
+ static RawBool* CaseInsensitiveCompareUC16(
+ RawString* str_raw,
+ RawSmi* lhs_index_raw,
+ RawSmi* rhs_index_raw,
+ RawSmi* length_raw);
+
+ static RawArray* Execute(const Function& function,
+ const String& input,
+ const Smi& start_offset,
+ Zone* zone);
+
+ virtual bool IsClosed() const { return (current_instruction_ == NULL); }
+
+ virtual intptr_t stack_limit_slack();
+ virtual void AdvanceCurrentPosition(intptr_t by);
+ virtual void AdvanceRegister(intptr_t reg, intptr_t by);
+ virtual void Backtrack();
+ virtual void BindBlock(BlockLabel* label);
+ virtual void CheckAtStart(BlockLabel* on_at_start);
+ virtual void CheckCharacter(uint32_t c, BlockLabel* on_equal);
+ virtual void CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ BlockLabel* on_equal);
+ virtual void CheckCharacterGT(uint16_t limit, BlockLabel* on_greater);
+ virtual void CheckCharacterLT(uint16_t limit, BlockLabel* on_less);
+ // A "greedy loop" is a loop that is both greedy and with a simple
+ // body. It has a particularly simple implementation.
+ virtual void CheckGreedyLoop(BlockLabel* on_tos_equals_current_position);
+ virtual void CheckNotAtStart(BlockLabel* on_not_at_start);
+ virtual void CheckNotBackReference(intptr_t start_reg,
+ BlockLabel* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(intptr_t start_reg,
+ BlockLabel* on_no_match);
+ virtual void CheckNotCharacter(uint32_t c, BlockLabel* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ BlockLabel* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uint16_t c,
+ uint16_t minus,
+ uint16_t mask,
+ BlockLabel* on_not_equal);
+ virtual void CheckCharacterInRange(uint16_t from,
+ uint16_t to,
+ BlockLabel* on_in_range);
+ virtual void CheckCharacterNotInRange(uint16_t from,
+ uint16_t to,
+ BlockLabel* on_not_in_range);
+ virtual void CheckBitInTable(const TypedData& table, BlockLabel* on_bit_set);
+
+ // Checks whether the given offset from the current position is before
+ // the end of the string.
+ virtual void CheckPosition(intptr_t cp_offset, BlockLabel* on_outside_input);
+ virtual bool CheckSpecialCharacterClass(
+ uint16_t type, BlockLabel* on_no_match);
+ virtual void Fail();
+ virtual void IfRegisterGE(intptr_t reg,
+ intptr_t comparand, BlockLabel* if_ge);
+ virtual void IfRegisterLT(intptr_t reg,
+ intptr_t comparand, BlockLabel* if_lt);
+ virtual void IfRegisterEqPos(intptr_t reg, BlockLabel* if_eq);
+ virtual IrregexpImplementation Implementation();
+ virtual void GoTo(BlockLabel* to);
+ virtual void LoadCurrentCharacter(intptr_t cp_offset,
+ BlockLabel* on_end_of_input,
+ bool check_bounds = true,
+ intptr_t characters = 1);
+ virtual void PopCurrentPosition();
+ virtual void PopRegister(intptr_t register_index);
+ virtual void Print(const char* str);
+ virtual void PushBacktrack(BlockLabel* label);
+ virtual void PushCurrentPosition();
+ virtual void PushRegister(intptr_t register_index);
+ virtual void ReadCurrentPositionFromRegister(intptr_t reg);
+ virtual void ReadStackPointerFromRegister(intptr_t reg);
+ virtual void SetCurrentPositionFromEnd(intptr_t by);
+ virtual void SetRegister(intptr_t register_index, intptr_t to);
+ virtual bool Succeed();
+ virtual void WriteCurrentPositionToRegister(intptr_t reg, intptr_t cp_offset);
+ virtual void ClearRegisters(intptr_t reg_from, intptr_t reg_to);
+ virtual void WriteStackPointerToRegister(intptr_t reg);
+
+ virtual void PrintBlocks();
+
+ IndirectGotoInstr* backtrack_goto() const { return backtrack_goto_; }
+ GraphEntryInstr* graph_entry() const { return entry_block_; }
+
+ intptr_t num_stack_locals() const { return local_id_.Count(); }
+ intptr_t num_blocks() const { return block_id_.Count(); }
+
+ // Generate a dispatch block implementing backtracking. Must be done after
+ // graph construction.
+ void GenerateBacktrackBlock();
+
+ // Allocate the actual registers array once its size is known. Must be done
+ // after graph construction.
+ void FinalizeRegistersArray();
+
+ private:
+ // Generate the contents of preset blocks. The entry block is the entry point
+ // of the generated code.
+ void GenerateEntryBlock();
+ // Copies capture indices into the result area and returns true.
+ void GenerateSuccessBlock();
+ // Returns false.
+ void GenerateExitBlock();
+
+ enum ComparisonKind {
+ kEQ,
+ kNE,
+ kLT,
+ kGT,
+ kLTE,
+ kGTE,
+ };
+
+ struct InstanceCallDescriptor {
+ // Standard (i.e. most non-Smi) functions.
+ explicit InstanceCallDescriptor(const String& name)
+ : name(name),
+ token_kind(Token::kILLEGAL),
+ checked_argument_count(1) { }
+
+ InstanceCallDescriptor(const String& name,
+ Token::Kind token_kind,
+ intptr_t checked_argument_count)
+ : name(name),
+ token_kind(token_kind),
+ checked_argument_count(checked_argument_count) { }
+
+ // Special cases for Smi and indexing functions.
+ static InstanceCallDescriptor FromToken(Token::Kind token_kind) {
+ switch (token_kind) {
+ case Token::kEQ: return InstanceCallDescriptor(
+ Symbols::EqualOperator(), token_kind, 2);
+ case Token::kADD: return InstanceCallDescriptor(
+ Symbols::Plus(), token_kind, 2);
+ case Token::kSUB: return InstanceCallDescriptor(
+ Symbols::Minus(), token_kind, 2);
+ case Token::kBIT_OR: return InstanceCallDescriptor(
+ Symbols::BitOr(), token_kind, 2);
+ case Token::kBIT_AND: return InstanceCallDescriptor(
+ Symbols::BitAnd(), token_kind, 2);
+ case Token::kLT: return InstanceCallDescriptor(
+ Symbols::LAngleBracket(), token_kind, 2);
+ case Token::kLTE: return InstanceCallDescriptor(
+ Symbols::LessEqualOperator(), token_kind, 2);
+ case Token::kGT: return InstanceCallDescriptor(
+ Symbols::RAngleBracket(), token_kind, 2);
+ case Token::kGTE: return InstanceCallDescriptor(
+ Symbols::GreaterEqualOperator(), token_kind, 2);
+ case Token::kNEGATE: return InstanceCallDescriptor(
+ Symbols::UnaryMinus(), token_kind, 1);
+ case Token::kINDEX: return InstanceCallDescriptor(
+ Symbols::IndexToken(), token_kind, 2);
+ case Token::kASSIGN_INDEX: return InstanceCallDescriptor(
+ Symbols::AssignIndexToken(), token_kind, 2);
+ default:
+ UNREACHABLE();
+ }
+ UNREACHABLE();
+ return InstanceCallDescriptor(Symbols::Empty());
+ }
+
+ const String& name;
+ Token::Kind token_kind;
+ intptr_t checked_argument_count;
+ };
+
+ LocalVariable* Local(const String& name);
+ LocalVariable* Parameter(const String& name, intptr_t index) const;
+
+ ConstantInstr* Int64Constant(int64_t value) const;
+ ConstantInstr* Uint64Constant(uint64_t value) const;
+ ConstantInstr* BoolConstant(bool value) const;
+ ConstantInstr* StringConstant(const char* value) const;
+
+ // The word character map static member of the RegExp class.
+ // Byte map of one byte characters with a 0xff if the character is a word
+ // character (digit, letter or underscore) and 0x00 otherwise.
+ // Used by generated RegExp code.
+ ConstantInstr* WordCharacterMapConstant() const;
+
+ ComparisonInstr* Comparison(ComparisonKind kind,
+ PushArgumentInstr* lhs,
+ PushArgumentInstr* rhs);
+ ComparisonInstr* Comparison(ComparisonKind kind,
+ Definition* lhs,
+ Definition* rhs);
+
+ InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc,
+ PushArgumentInstr* arg1) const;
+ InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc,
+ PushArgumentInstr* arg1,
+ PushArgumentInstr* arg2) const;
+ InstanceCallInstr* InstanceCall(const InstanceCallDescriptor& desc,
+ PushArgumentInstr* arg1,
+ PushArgumentInstr* arg2,
+ PushArgumentInstr* arg3) const;
+ InstanceCallInstr* InstanceCall(
+ const InstanceCallDescriptor& desc,
+ ZoneGrowableArray<PushArgumentInstr*>* arguments) const;
+
+ StaticCallInstr* StaticCall(const Function& function) const;
+ StaticCallInstr* StaticCall(const Function& function,
+ PushArgumentInstr* arg1) const;
+ StaticCallInstr* StaticCall(const Function& function,
+ PushArgumentInstr* arg1,
+ PushArgumentInstr* arg2) const;
+ StaticCallInstr* StaticCall(
+ const Function& function,
+ ZoneGrowableArray<PushArgumentInstr*>* arguments) const;
+
+ // Creates a new block consisting simply of a goto to dst.
+ TargetEntryInstr* TargetWithJoinGoto(JoinEntryInstr* dst);
+ IndirectEntryInstr* IndirectWithJoinGoto(JoinEntryInstr* dst);
+
+ // Adds, respectively subtracts lhs and rhs and returns the result.
+ Definition* Add(PushArgumentInstr* lhs, PushArgumentInstr* rhs);
+ Definition* Sub(PushArgumentInstr* lhs, PushArgumentInstr* rhs);
+
+ LoadLocalInstr* LoadLocal(LocalVariable* local) const;
+ void StoreLocal(LocalVariable* local, Value* value);
+
+ PushArgumentInstr* PushArgument(Value* value);
+ PushArgumentInstr* PushLocal(LocalVariable* local);
+
+ PushArgumentInstr* PushRegisterIndex(intptr_t reg);
+ Value* LoadRegister(intptr_t reg);
+ void StoreRegister(intptr_t reg, intptr_t value);
+ void StoreRegister(PushArgumentInstr* registers,
+ PushArgumentInstr* index,
+ PushArgumentInstr* value);
+
+ // Load a number of characters at the given offset from the
+ // current position, into the current-character register.
+ void LoadCurrentCharacterUnchecked(intptr_t cp_offset,
+ intptr_t character_count);
+
+ // Returns the character within the passed string at the specified index.
+ Value* CharacterAt(LocalVariable* index);
+
+ // Load a number of characters starting from index in the pattern string.
+ Value* LoadCodeUnitsAt(LocalVariable* index, intptr_t character_count);
+
+ // Check whether preemption has been requested.
+ void CheckPreemption();
+
+ // Byte size of chars in the string to match (decided by the Mode argument)
+ inline intptr_t char_size() { return static_cast<int>(mode_); }
+
+ // Equivalent to a conditional branch to the label, unless the label
+ // is NULL, in which case it is a conditional Backtrack.
+ void BranchOrBacktrack(ComparisonInstr* comparison,
+ BlockLabel* true_successor);
+
+ // Set up all local variables and parameters.
+ void InitializeLocals();
+
+ // Allocates a new local, and returns the appropriate id for placing it
+ // on the stack.
+ intptr_t GetNextLocalIndex();
+
+ // We never have any copied parameters.
+ intptr_t num_copied_params() const {
+ return 0;
+ }
+
+ // Return the position register at the specified index, creating it if
+ // necessary. Note that the number of such registers can exceed the amount
+ // required by the number of output captures.
+ LocalVariable* position_register(intptr_t index);
+
+ void set_current_instruction(Instruction* instruction);
+
+ // The following functions are responsible for appending instructions
+ // to the current instruction in various ways. The most simple one
+ // is AppendInstruction, which simply appends an instruction and performs
+ // bookkeeping.
+ void AppendInstruction(Instruction* instruction);
+ // Similar to AppendInstruction, but closes the current block by
+ // setting current_instruction_ to NULL.
+ void CloseBlockWith(Instruction* instruction);
+ // Appends definition and allocates a temp index for the result.
+ Value* Bind(Definition* definition);
+ // Loads and binds a local variable.
+ Value* BindLoadLocal(const LocalVariable& local);
+
+ // Appends the definition.
+ void Do(Definition* definition);
+ // Closes the current block with a jump to the specified block.
+ void GoTo(JoinEntryInstr* to);
+
+ // Accessors for our local stack_.
+ void PushStack(Definition* definition);
+ Definition* PopStack();
+ Definition* PeekStack();
+ void CheckStackLimit();
+ void GrowStack();
+
+ // Prints the specified argument. Used for debugging.
+ void Print(PushArgumentInstr* argument);
+
+ // A utility class tracking ids of various objects such as blocks, temps, etc.
+ class IdAllocator : public ValueObject {
+ public:
+ IdAllocator() : next_id(0) { }
+
+ intptr_t Count() const { return next_id; }
+ intptr_t Alloc(intptr_t count = 1) {
+ ASSERT(count >= 0);
+ intptr_t current_id = next_id;
+ next_id += count;
+ return current_id;
+ }
+ void Dealloc(intptr_t count = 1) {
+ ASSERT(count <= next_id);
+ next_id -= count;
+ }
+
+ private:
+ intptr_t next_id;
+ };
+
+ // Which mode to generate code for (ASCII or UC16).
+ Mode mode_;
+
+ // Which specific string class to generate code for.
+ intptr_t specialization_cid_;
+
+ // Block entries used internally.
+ GraphEntryInstr* entry_block_;
+ JoinEntryInstr* start_block_;
+ JoinEntryInstr* success_block_;
+ JoinEntryInstr* exit_block_;
+
+ // Shared backtracking block.
+ JoinEntryInstr* backtrack_block_;
+ // Single indirect goto instruction which performs all backtracking.
+ IndirectGotoInstr* backtrack_goto_;
+
+ const ParsedFunction* parsed_function_;
+ const ZoneGrowableArray<const ICData*>& ic_data_array_;
+
+ // All created blocks are contained within this set. Used for printing
+ // the generated code.
+ GrowableArray<BlockEntryInstr*> blocks_;
+
+ // The current instruction to link to when new code is emitted.
+ Instruction* current_instruction_;
+
+ // A list, acting as the runtime stack for both backtrack locations and
+ // stored positions within the string.
+ LocalVariable* stack_;
+ LocalVariable* stack_pointer_;
+
+ // Stores the current character within the string.
+ LocalVariable* current_character_;
+
+ // Stores the current location within the string as a negative offset
+ // from the end of the string.
+ LocalVariable* current_position_;
+
+ // The string being processed, passed as a function parameter.
+ LocalVariable* string_param_;
+
+ // Stores the length of string_param_.
+ LocalVariable* string_param_length_;
+
+ // The start index within the string, passed as a function parameter.
+ LocalVariable* start_index_param_;
+
+ // An assortment of utility variables.
+ LocalVariable* capture_length_;
+ LocalVariable* match_start_index_;
+ LocalVariable* capture_start_index_;
+ LocalVariable* match_end_index_;
+ LocalVariable* char_in_capture_;
+ LocalVariable* char_in_match_;
+ LocalVariable* index_temp_;
+
+ LocalVariable* result_;
+
+ // Stored positions containing group bounds. Generated as needed.
+ LocalVariable* registers_;
+ intptr_t registers_count_;
+ const intptr_t saved_registers_count_;
+
+ // The actual array objects used for the stack and registers.
+ Array& stack_array_cell_;
+ TypedData& registers_array_;
+
+ IdAllocator block_id_;
+ IdAllocator temp_id_;
+ IdAllocator arg_id_;
+ IdAllocator local_id_;
+ IdAllocator indirect_id_;
+};
+
+
+} // namespace dart
+
+#endif // VM_REGEXP_ASSEMBLER_IR_H_
diff --git a/runtime/vm/regexp_bytecodes.h b/runtime/vm/regexp_bytecodes.h
new file mode 100644
index 0000000..8d1cddb
--- /dev/null
+++ b/runtime/vm/regexp_bytecodes.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2015, 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 VM_REGEXP_BYTECODES_H_
+#define VM_REGEXP_BYTECODES_H_
+
+namespace dart {
+
+const int BYTECODE_MASK = 0xff;
+// The first argument is packed in with the byte code in one word, but so it
+// has 24 bits, but it can be positive and negative so only use 23 bits for
+// positive values.
+const unsigned int MAX_FIRST_ARG = 0x7fffffu;
+const int BYTECODE_SHIFT = 8;
+
+#define BYTECODE_ITERATOR(V) \
+V(BREAK, 0, 4) /* bc8 */ \
+V(PUSH_CP, 1, 4) /* bc8 pad24 */ \
+V(PUSH_BT, 2, 8) /* bc8 pad24 offset32 */ \
+V(PUSH_REGISTER, 3, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER_TO_CP, 4, 8) /* bc8 reg_idx24 offset32 */ \
+V(SET_CP_TO_REGISTER, 5, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER_TO_SP, 6, 4) /* bc8 reg_idx24 */ \
+V(SET_SP_TO_REGISTER, 7, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER, 8, 8) /* bc8 reg_idx24 value32 */ \
+V(ADVANCE_REGISTER, 9, 8) /* bc8 reg_idx24 value32 */ \
+V(POP_CP, 10, 4) /* bc8 pad24 */ \
+V(POP_BT, 11, 4) /* bc8 pad24 */ \
+V(POP_REGISTER, 12, 4) /* bc8 reg_idx24 */ \
+V(FAIL, 13, 4) /* bc8 pad24 */ \
+V(SUCCEED, 14, 4) /* bc8 pad24 */ \
+V(ADVANCE_CP, 15, 4) /* bc8 offset24 */ \
+V(GOTO, 16, 8) /* bc8 pad24 addr32 */ \
+V(LOAD_CURRENT_CHAR, 17, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_CURRENT_CHAR_UNCHECKED, 18, 4) /* bc8 offset24 */ \
+V(LOAD_2_CURRENT_CHARS, 19, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_2_CURRENT_CHARS_UNCHECKED, 20, 4) /* bc8 offset24 */ \
+V(LOAD_4_CURRENT_CHARS, 21, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_4_CURRENT_CHARS_UNCHECKED, 22, 4) /* bc8 offset24 */ \
+V(CHECK_4_CHARS, 23, 12) /* bc8 pad24 uint32 addr32 */ \
+V(CHECK_CHAR, 24, 8) /* bc8 pad8 uint16 addr32 */ \
+V(CHECK_NOT_4_CHARS, 25, 12) /* bc8 pad24 uint32 addr32 */ \
+V(CHECK_NOT_CHAR, 26, 8) /* bc8 pad8 uint16 addr32 */ \
+V(AND_CHECK_4_CHARS, 27, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
+V(AND_CHECK_CHAR, 28, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
+V(AND_CHECK_NOT_4_CHARS, 29, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
+V(AND_CHECK_NOT_CHAR, 30, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
+V(MINUS_AND_CHECK_NOT_CHAR, 31, 12) /* bc8 pad8 uc16 uc16 uc16 addr32 */ \
+V(CHECK_CHAR_IN_RANGE, 32, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
+V(CHECK_CHAR_NOT_IN_RANGE, 33, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
+V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
+V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
+V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
+V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_NOT_REGS_EQUAL, 39, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
+V(CHECK_REGISTER_LT, 40, 12) /* bc8 reg_idx24 value32 addr32 */ \
+V(CHECK_REGISTER_GE, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
+V(CHECK_REGISTER_EQ_POS, 42, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \
+V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
+V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \
+V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \
+V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */
+
+#define DECLARE_BYTECODES(name, code, length) \
+ static const int BC_##name = code;
+BYTECODE_ITERATOR(DECLARE_BYTECODES)
+#undef DECLARE_BYTECODES
+
+#define DECLARE_BYTECODE_LENGTH(name, code, length) \
+ static const int BC_##name##_LENGTH = length;
+BYTECODE_ITERATOR(DECLARE_BYTECODE_LENGTH)
+#undef DECLARE_BYTECODE_LENGTH
+
+} // namespace dart
+
+#endif // VM_REGEXP_BYTECODES_H_
diff --git a/runtime/vm/regexp_interpreter.cc b/runtime/vm/regexp_interpreter.cc
new file mode 100644
index 0000000..b036ec8
--- /dev/null
+++ b/runtime/vm/regexp_interpreter.cc
@@ -0,0 +1,621 @@
+// Copyright (c) 2015, 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.
+
+// A simple interpreter for the Irregexp byte code.
+
+#include "vm/regexp_interpreter.h"
+
+#include "vm/regexp_bytecodes.h"
+#include "vm/regexp_assembler.h"
+#include "vm/object.h"
+#include "vm/unicode.h"
+#include "vm/unibrow.h"
+#include "vm/unibrow-inl.h"
+
+namespace dart {
+
+DEFINE_FLAG(bool, trace_regexp_bytecodes, false, "trace_regexp_bytecodes");
+
+typedef unibrow::Mapping<unibrow::Ecma262Canonicalize> Canonicalize;
+
+template<typename Char>
+static bool BackRefMatchesNoCase(Canonicalize* interp_canonicalize,
+ intptr_t from,
+ intptr_t current,
+ intptr_t len,
+ const String& subject);
+
+template <>
+bool BackRefMatchesNoCase<uint16_t>(Canonicalize* interp_canonicalize,
+ intptr_t from,
+ intptr_t current,
+ intptr_t len,
+ const String& subject) {
+ for (int i = 0; i < len; i++) {
+ int32_t old_char = subject.CharAt(from++);
+ int32_t new_char = subject.CharAt(current++);
+ if (old_char == new_char) continue;
+ int32_t old_string[1] = { old_char };
+ int32_t new_string[1] = { new_char };
+ interp_canonicalize->get(old_char, '\0', old_string);
+ interp_canonicalize->get(new_char, '\0', new_string);
+ if (old_string[0] != new_string[0]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <>
+bool BackRefMatchesNoCase<uint8_t>(Canonicalize* interp_canonicalize,
+ intptr_t from,
+ intptr_t current,
+ intptr_t len,
+ const String& subject) {
+ for (int i = 0; i < len; i++) {
+ unsigned int old_char = subject.CharAt(from++);
+ unsigned int new_char = subject.CharAt(current++);
+ if (old_char == new_char) continue;
+ // Convert both characters to lower case.
+ old_char |= 0x20;
+ new_char |= 0x20;
+ if (old_char != new_char) return false;
+ // Not letters in the ASCII range and Latin-1 range.
+ if (!(old_char - 'a' <= 'z' - 'a') &&
+ !(old_char - 224 <= 254 - 224 && old_char != 247)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+#ifdef DEBUG
+static void TraceInterpreter(const uint8_t* code_base,
+ const uint8_t* pc,
+ int stack_depth,
+ int current_position,
+ uint32_t current_char,
+ int bytecode_length,
+ const char* bytecode_name) {
+ if (FLAG_trace_regexp_bytecodes) {
+ bool printable = (current_char < 127 && current_char >= 32);
+ const char* format =
+ printable ?
+ "pc = %02x, sp = %d, curpos = %d, curchar = %08x (%c), bc = %s" :
+ "pc = %02x, sp = %d, curpos = %d, curchar = %08x .%c., bc = %s";
+ OS::Print(format,
+ pc - code_base,
+ stack_depth,
+ current_position,
+ current_char,
+ printable ? current_char : '.',
+ bytecode_name);
+ for (int i = 0; i < bytecode_length; i++) {
+ OS::Print(", %02x", pc[i]);
+ }
+ OS::Print(" ");
+ for (int i = 1; i < bytecode_length; i++) {
+ unsigned char b = pc[i];
+ if (b < 127 && b >= 32) {
+ OS::Print("%c", b);
+ } else {
+ OS::Print(".");
+ }
+ }
+ OS::Print("\n");
+ }
+}
+
+
+#define BYTECODE(name) \
+ case BC_##name: \
+ TraceInterpreter(code_base, \
+ pc, \
+ static_cast<int>(backtrack_sp - backtrack_stack_base), \
+ current, \
+ current_char, \
+ BC_##name##_LENGTH, \
+ #name);
+#else
+#define BYTECODE(name) \
+ case BC_##name:
+#endif
+
+
+static int32_t Load32Aligned(const uint8_t* pc) {
+ ASSERT((reinterpret_cast<intptr_t>(pc) & 3) == 0);
+ return *reinterpret_cast<const int32_t *>(pc);
+}
+
+
+static int32_t Load16Aligned(const uint8_t* pc) {
+ ASSERT((reinterpret_cast<intptr_t>(pc) & 1) == 0);
+ return *reinterpret_cast<const uint16_t *>(pc);
+}
+
+
+// A simple abstraction over the backtracking stack used by the interpreter.
+// This backtracking stack does not grow automatically, but it ensures that the
+// the memory held by the stack is released or remembered in a cache if the
+// matching terminates.
+class BacktrackStack {
+ public:
+ explicit BacktrackStack(Zone* zone) {
+ data_ = zone->Alloc<intptr_t>(kBacktrackStackSize);
+ }
+
+ intptr_t* data() const { return data_; }
+
+ intptr_t max_size() const { return kBacktrackStackSize; }
+
+ private:
+ static const intptr_t kBacktrackStackSize = 10000;
+
+ intptr_t* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(BacktrackStack);
+};
+
+
+template <typename Char>
+static IrregexpInterpreter::IrregexpResult RawMatch(const uint8_t* code_base,
+ const String& subject,
+ int32_t* registers,
+ intptr_t current,
+ uint32_t current_char,
+ Zone* zone) {
+ const uint8_t* pc = code_base;
+ // BacktrackStack ensures that the memory allocated for the backtracking stack
+ // is returned to the system or cached if there is no stack being cached at
+ // the moment.
+ BacktrackStack backtrack_stack(zone);
+ intptr_t* backtrack_stack_base = backtrack_stack.data();
+ intptr_t* backtrack_sp = backtrack_stack_base;
+ intptr_t backtrack_stack_space = backtrack_stack.max_size();
+
+ // TODO(zerny): Optimize as single instance. V8 has this as an
+ // isolate member.
+ unibrow::Mapping<unibrow::Ecma262Canonicalize> canonicalize;
+
+ intptr_t subject_length = subject.Length();
+
+#ifdef DEBUG
+ if (FLAG_trace_regexp_bytecodes) {
+ OS::Print("Start irregexp bytecode interpreter\n");
+ }
+#endif
+ while (true) {
+ int32_t insn = Load32Aligned(pc);
+ switch (insn & BYTECODE_MASK) {
+ BYTECODE(BREAK)
+ UNREACHABLE();
+ return IrregexpInterpreter::RE_FAILURE;
+ BYTECODE(PUSH_CP)
+ if (--backtrack_stack_space < 0) {
+ return IrregexpInterpreter::RE_EXCEPTION;
+ }
+ *backtrack_sp++ = current;
+ pc += BC_PUSH_CP_LENGTH;
+ break;
+ BYTECODE(PUSH_BT)
+ if (--backtrack_stack_space < 0) {
+ return IrregexpInterpreter::RE_EXCEPTION;
+ }
+ *backtrack_sp++ = Load32Aligned(pc + 4);
+ pc += BC_PUSH_BT_LENGTH;
+ break;
+ BYTECODE(PUSH_REGISTER)
+ if (--backtrack_stack_space < 0) {
+ return IrregexpInterpreter::RE_EXCEPTION;
+ }
+ *backtrack_sp++ = registers[insn >> BYTECODE_SHIFT];
+ pc += BC_PUSH_REGISTER_LENGTH;
+ break;
+ BYTECODE(SET_REGISTER)
+ registers[insn >> BYTECODE_SHIFT] = Load32Aligned(pc + 4);
+ pc += BC_SET_REGISTER_LENGTH;
+ break;
+ BYTECODE(ADVANCE_REGISTER)
+ registers[insn >> BYTECODE_SHIFT] += Load32Aligned(pc + 4);
+ pc += BC_ADVANCE_REGISTER_LENGTH;
+ break;
+ BYTECODE(SET_REGISTER_TO_CP)
+ registers[insn >> BYTECODE_SHIFT] = current + Load32Aligned(pc + 4);
+ pc += BC_SET_REGISTER_TO_CP_LENGTH;
+ break;
+ BYTECODE(SET_CP_TO_REGISTER)
+ current = registers[insn >> BYTECODE_SHIFT];
+ pc += BC_SET_CP_TO_REGISTER_LENGTH;
+ break;
+ BYTECODE(SET_REGISTER_TO_SP)
+ registers[insn >> BYTECODE_SHIFT] =
+ static_cast<int>(backtrack_sp - backtrack_stack_base);
+ pc += BC_SET_REGISTER_TO_SP_LENGTH;
+ break;
+ BYTECODE(SET_SP_TO_REGISTER)
+ backtrack_sp = backtrack_stack_base + registers[insn >> BYTECODE_SHIFT];
+ backtrack_stack_space = backtrack_stack.max_size() -
+ static_cast<int>(backtrack_sp - backtrack_stack_base);
+ pc += BC_SET_SP_TO_REGISTER_LENGTH;
+ break;
+ BYTECODE(POP_CP)
+ backtrack_stack_space++;
+ --backtrack_sp;
+ current = *backtrack_sp;
+ pc += BC_POP_CP_LENGTH;
+ break;
+ BYTECODE(POP_BT)
+ backtrack_stack_space++;
+ --backtrack_sp;
+ pc = code_base + *backtrack_sp;
+ break;
+ BYTECODE(POP_REGISTER)
+ backtrack_stack_space++;
+ --backtrack_sp;
+ registers[insn >> BYTECODE_SHIFT] = *backtrack_sp;
+ pc += BC_POP_REGISTER_LENGTH;
+ break;
+ BYTECODE(FAIL)
+ return IrregexpInterpreter::RE_FAILURE;
+ BYTECODE(SUCCEED)
+ return IrregexpInterpreter::RE_SUCCESS;
+ BYTECODE(ADVANCE_CP)
+ current += insn >> BYTECODE_SHIFT;
+ pc += BC_ADVANCE_CP_LENGTH;
+ break;
+ BYTECODE(GOTO)
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ BYTECODE(ADVANCE_CP_AND_GOTO)
+ current += insn >> BYTECODE_SHIFT;
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ BYTECODE(CHECK_GREEDY)
+ if (current == backtrack_sp[-1]) {
+ backtrack_sp--;
+ backtrack_stack_space++;
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_GREEDY_LENGTH;
+ }
+ break;
+ BYTECODE(LOAD_CURRENT_CHAR) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ if (pos >= subject_length) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ current_char = subject.CharAt(pos);
+ pc += BC_LOAD_CURRENT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(LOAD_CURRENT_CHAR_UNCHECKED) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ current_char = subject.CharAt(pos);
+ pc += BC_LOAD_CURRENT_CHAR_UNCHECKED_LENGTH;
+ break;
+ }
+ BYTECODE(LOAD_2_CURRENT_CHARS) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ if (pos + 2 > subject_length) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ Char next = subject.CharAt(pos + 1);
+ current_char = subject.CharAt(pos) |
+ (next << (kBitsPerByte * sizeof(Char)));
+ pc += BC_LOAD_2_CURRENT_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(LOAD_2_CURRENT_CHARS_UNCHECKED) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ Char next = subject.CharAt(pos + 1);
+ current_char = subject.CharAt(pos) |
+ (next << (kBitsPerByte * sizeof(Char)));
+ pc += BC_LOAD_2_CURRENT_CHARS_UNCHECKED_LENGTH;
+ break;
+ }
+ BYTECODE(LOAD_4_CURRENT_CHARS) {
+ ASSERT(sizeof(Char) == 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ if (pos + 4 > subject_length) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ Char next1 = subject.CharAt(pos + 1);
+ Char next2 = subject.CharAt(pos + 2);
+ Char next3 = subject.CharAt(pos + 3);
+ current_char = (subject.CharAt(pos) |
+ (next1 << 8) |
+ (next2 << 16) |
+ (next3 << 24));
+ pc += BC_LOAD_4_CURRENT_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(LOAD_4_CURRENT_CHARS_UNCHECKED) {
+ ASSERT(sizeof(Char) == 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ Char next1 = subject.CharAt(pos + 1);
+ Char next2 = subject.CharAt(pos + 2);
+ Char next3 = subject.CharAt(pos + 3);
+ current_char = (subject.CharAt(pos) |
+ (next1 << 8) |
+ (next2 << 16) |
+ (next3 << 24));
+ pc += BC_LOAD_4_CURRENT_CHARS_UNCHECKED_LENGTH;
+ break;
+ }
+ BYTECODE(CHECK_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c == current_char) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c == current_char) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_NOT_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c != current_char) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_NOT_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_NOT_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c != current_char) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_NOT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c == (current_char & Load32Aligned(pc + 8))) {
+ pc = code_base + Load32Aligned(pc + 12);
+ } else {
+ pc += BC_AND_CHECK_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c == (current_char & Load32Aligned(pc + 4))) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_AND_CHECK_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_NOT_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c != (current_char & Load32Aligned(pc + 8))) {
+ pc = code_base + Load32Aligned(pc + 12);
+ } else {
+ pc += BC_AND_CHECK_NOT_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_NOT_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c != (current_char & Load32Aligned(pc + 4))) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_AND_CHECK_NOT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(MINUS_AND_CHECK_NOT_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ uint32_t minus = Load16Aligned(pc + 4);
+ uint32_t mask = Load16Aligned(pc + 6);
+ if (c != ((current_char - minus) & mask)) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_MINUS_AND_CHECK_NOT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_CHAR_IN_RANGE) {
+ uint32_t from = Load16Aligned(pc + 4);
+ uint32_t to = Load16Aligned(pc + 6);
+ if (from <= current_char && current_char <= to) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_CHAR_IN_RANGE_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_CHAR_NOT_IN_RANGE) {
+ uint32_t from = Load16Aligned(pc + 4);
+ uint32_t to = Load16Aligned(pc + 6);
+ if (from > current_char || current_char > to) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_CHAR_NOT_IN_RANGE_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_BIT_IN_TABLE) {
+ int mask = RegExpMacroAssembler::kTableMask;
+ uint8_t b = pc[8 + ((current_char & mask) >> kBitsPerByteLog2)];
+ int bit = (current_char & (kBitsPerByte - 1));
+ if ((b & (1 << bit)) != 0) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_BIT_IN_TABLE_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_LT) {
+ uint32_t limit = (insn >> BYTECODE_SHIFT);
+ if (current_char < limit) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_LT_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_GT) {
+ uint32_t limit = (insn >> BYTECODE_SHIFT);
+ if (current_char > limit) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_GT_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_REGISTER_LT)
+ if (registers[insn >> BYTECODE_SHIFT] < Load32Aligned(pc + 4)) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_REGISTER_LT_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_REGISTER_GE)
+ if (registers[insn >> BYTECODE_SHIFT] >= Load32Aligned(pc + 4)) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_REGISTER_GE_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_REGISTER_EQ_POS)
+ if (registers[insn >> BYTECODE_SHIFT] == current) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_REGISTER_EQ_POS_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_NOT_REGS_EQUAL)
+ if (registers[insn >> BYTECODE_SHIFT] ==
+ registers[Load32Aligned(pc + 4)]) {
+ pc += BC_CHECK_NOT_REGS_EQUAL_LENGTH;
+ } else {
+ pc = code_base + Load32Aligned(pc + 8);
+ }
+ break;
+ BYTECODE(CHECK_NOT_BACK_REF) {
+ int from = registers[insn >> BYTECODE_SHIFT];
+ int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
+ if (from < 0 || len <= 0) {
+ pc += BC_CHECK_NOT_BACK_REF_LENGTH;
+ break;
+ }
+ if (current + len > subject_length) {
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ } else {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (subject.CharAt(from + i) != subject.CharAt(current + i)) {
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ }
+ }
+ if (i < len) break;
+ current += len;
+ }
+ pc += BC_CHECK_NOT_BACK_REF_LENGTH;
+ break;
+ }
+ BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
+ int from = registers[insn >> BYTECODE_SHIFT];
+ int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
+ if (from < 0 || len <= 0) {
+ pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
+ break;
+ }
+ if (current + len > subject_length) {
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ } else {
+ if (BackRefMatchesNoCase<Char>(&canonicalize,
+ from, current, len, subject)) {
+ current += len;
+ pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
+ } else {
+ pc = code_base + Load32Aligned(pc + 4);
+ }
+ }
+ break;
+ }
+ BYTECODE(CHECK_AT_START)
+ if (current == 0) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_AT_START_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_NOT_AT_START)
+ if (current == 0) {
+ pc += BC_CHECK_NOT_AT_START_LENGTH;
+ } else {
+ pc = code_base + Load32Aligned(pc + 4);
+ }
+ break;
+ BYTECODE(SET_CURRENT_POSITION_FROM_END) {
+ int by = static_cast<uint32_t>(insn) >> BYTECODE_SHIFT;
+ if (subject_length - current > by) {
+ current = subject_length - by;
+ current_char = subject.CharAt(current - 1);
+ }
+ pc += BC_SET_CURRENT_POSITION_FROM_END_LENGTH;
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+IrregexpInterpreter::IrregexpResult IrregexpInterpreter::Match(
+ const TypedData& bytecode,
+ const String& subject,
+ int32_t* registers,
+ intptr_t start_position,
+ Zone* zone) {
+ NoSafepointScope no_safepoint;
+ const uint8_t* code_base = reinterpret_cast<uint8_t*>(bytecode.DataAddr(0));
+
+ uint16_t previous_char = '\n';
+ if (start_position != 0) {
+ previous_char = subject.CharAt(start_position - 1);
+ }
+
+ if (subject.IsOneByteString() || subject.IsExternalOneByteString()) {
+ return RawMatch<uint8_t>(code_base,
+ subject,
+ registers,
+ start_position,
+ previous_char,
+ zone);
+ } else if (subject.IsTwoByteString() || subject.IsExternalTwoByteString()) {
+ return RawMatch<uint16_t>(code_base,
+ subject,
+ registers,
+ start_position,
+ previous_char,
+ zone);
+ } else {
+ UNREACHABLE();
+ return IrregexpInterpreter::RE_FAILURE;
+ }
+}
+
+} // namespace dart
diff --git a/runtime/vm/regexp_interpreter.h b/runtime/vm/regexp_interpreter.h
new file mode 100644
index 0000000..eab89cb
--- /dev/null
+++ b/runtime/vm/regexp_interpreter.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2015, 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.
+
+// A simple interpreter for the Irregexp byte code.
+
+#ifndef VM_REGEXP_INTERPRETER_H_
+#define VM_REGEXP_INTERPRETER_H_
+
+#include "vm/allocation.h"
+#include "vm/object.h"
+#include "vm/zone.h"
+
+namespace dart {
+
+class IrregexpInterpreter : public AllStatic {
+ public:
+ enum IrregexpResult {
+ RE_FAILURE = 0,
+ RE_SUCCESS = 1,
+ RE_EXCEPTION = -1
+ };
+
+ static IrregexpResult Match(const TypedData& bytecode,
+ const String& subject,
+ int32_t* captures,
+ intptr_t start_position,
+ Zone* zone);
+};
+
+} // namespace dart
+
+#endif // VM_REGEXP_INTERPRETER_H_
diff --git a/runtime/vm/regexp_test.cc b/runtime/vm/regexp_test.cc
index 4796279..7b33982 100644
--- a/runtime/vm/regexp_test.cc
+++ b/runtime/vm/regexp_test.cc
@@ -7,6 +7,7 @@
#include "vm/isolate.h"
#include "vm/object.h"
#include "vm/regexp.h"
+#include "vm/regexp_assembler_ir.h"
#include "vm/unit_test.h"
namespace dart {
diff --git a/runtime/vm/resolver.cc b/runtime/vm/resolver.cc
index 89d9ff6..954f340 100644
--- a/runtime/vm/resolver.cc
+++ b/runtime/vm/resolver.cc
@@ -14,7 +14,7 @@
namespace dart {
DEFINE_FLAG(bool, trace_resolving, false, "Trace resolving.");
-
+DECLARE_FLAG(bool, lazy_dispatchers);
// The actual names of named arguments are not checked by the dynamic resolver,
// but by the method entry code. It is important that the dynamic resolver
@@ -66,6 +66,7 @@
// owning method M.
static RawFunction* CreateMethodExtractor(const String& getter_name,
const Function& method) {
+ ASSERT(FLAG_lazy_dispatchers);
const Function& closure_function =
Function::Handle(method.ImplicitClosureFunction());
@@ -124,13 +125,15 @@
return function.raw();
}
// Getter invocation might actually be a method extraction.
- if (is_getter && function.IsNull()) {
- function ^= cls.LookupDynamicFunction(field_name);
- if (!function.IsNull()) {
- // We were looking for the getter but found a method with the same name.
- // Create a method extractor and return it.
- function ^= CreateMethodExtractor(function_name, function);
- return function.raw();
+ if (FLAG_lazy_dispatchers) {
+ if (is_getter && function.IsNull()) {
+ function ^= cls.LookupDynamicFunction(field_name);
+ if (!function.IsNull()) {
+ // We were looking for the getter but found a method with the same
+ // name. Create a method extractor and return it.
+ function ^= CreateMethodExtractor(function_name, function);
+ return function.raw();
+ }
}
}
cls = cls.SuperClass();
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 135c559..5bcf2ff 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -1567,11 +1567,11 @@
// and then fill it up with the script objects.
ASSERT(isolate_ != NULL);
ScriptVisitor scripts_counter(isolate_);
- heap->old_space()->VisitObjects(&scripts_counter);
+ heap->IterateOldObjects(&scripts_counter);
intptr_t count = scripts_counter.count();
scripts_ = Array::New(count, Heap::kOld);
ScriptVisitor script_visitor(isolate_, &scripts_);
- heap->old_space()->VisitObjects(&script_visitor);
+ heap->IterateOldObjects(&script_visitor);
// Stash the symbol table away for writing and reading into the vm isolate,
// and reset the symbol table for the regular isolate so that we do not
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 68f4751..ee38d31 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -311,6 +311,11 @@
V(TwoNewlines, "\n\n") \
V(TwoSpaces, " ") \
V(_instanceOf, "_instanceOf") \
+ V(_instanceOfSmi, "_instanceOfSmi") \
+ V(_instanceOfNum, "_instanceOfNum") \
+ V(_instanceOfInt, "_instanceOfInt") \
+ V(_instanceOfDouble, "_instanceOfDouble") \
+ V(_instanceOfString, "_instanceOfString") \
V(_as, "_as") \
V(GetterPrefix, "get:") \
V(SetterPrefix, "set:") \
diff --git a/runtime/vm/verifier.cc b/runtime/vm/verifier.cc
index 3c9fa66..556a738 100644
--- a/runtime/vm/verifier.cc
+++ b/runtime/vm/verifier.cc
@@ -78,9 +78,9 @@
isolate->heap()->CreateAllocatedObjectSet(mark_expectation);
VerifyPointersVisitor visitor(isolate, allocated_set);
// Visit all strongly reachable objects.
- isolate->VisitObjectPointers(&visitor,
- false, // skip prologue weak handles
- StackFrameIterator::kValidateFrames);
+ isolate->IterateObjectPointers(&visitor,
+ false, // skip prologue weak handles
+ StackFrameIterator::kValidateFrames);
VerifyWeakPointersVisitor weak_visitor(&visitor);
// Visit weak handles and prologue weak handles.
isolate->VisitWeakPersistentHandles(&weak_visitor,
diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi
index a8fb214..886e14c 100644
--- a/runtime/vm/vm_sources.gypi
+++ b/runtime/vm/vm_sources.gypi
@@ -351,8 +351,16 @@
'regexp.h',
'regexp_assembler.cc',
'regexp_assembler.h',
+ 'regexp_assembler_bytecode.cc',
+ 'regexp_assembler_bytecode.h',
+ 'regexp_assembler_bytecode_inl.h',
+ 'regexp_assembler_ir.cc',
+ 'regexp_assembler_ir.h',
'regexp_ast.cc',
'regexp_ast.h',
+ 'regexp_bytecodes.h',
+ 'regexp_interpreter.cc',
+ 'regexp_interpreter.h',
'regexp_parser.cc',
'regexp_parser.h',
'regexp_test.cc',
diff --git a/samples/samples.status b/samples/samples.status
index 224e573..81d9654 100644
--- a/samples/samples.status
+++ b/samples/samples.status
@@ -24,4 +24,3 @@
[ $arch == simarm64 ]
*: Skip
-[ $compiler == dart2js && $cps_ir ]
diff --git a/sdk/api_readme.md b/sdk/api_readme.md
index 68b662c..250c823 100644
--- a/sdk/api_readme.md
+++ b/sdk/api_readme.md
@@ -1,38 +1,31 @@
-Welcome to the Dart API reference documentation,
-covering the official Dart API libraries.
-
-Some of the most fundamental Dart libraries include:
+Welcome to the Dart API reference documentation, covering the official Dart API
+libraries. Some of the most fundamental Dart libraries include:
- * [dart:core](./dart_core/index.html):
- Core functionality such as strings, numbers, collections, errors,
- dates, and URIs.
- * [dart:html](./dart_html/index.html):
- DOM manipulation for web apps.
- * [dart:io](./dart_io/index.html):
- I/O for command-line apps.
+ * [dart:core](dart-core/index.html): Core functionality such as strings, numbers, collections, errors, dates, and URIs.
+ * [dart:html](dart-html/index.html): DOM manipulation for web apps.
+ * [dart:io](dart-io/index.html): I/O for command-line apps.
-Except for dart:core, you must import a library before you can use it.
-Here's an example of importing dart:html and dart:math:
-
- import 'dart:html';
- import 'dart:math';
-
-You can install more libraries
-using the _pub package manager_.
-For information on finding, using, and publishing libraries (and more)
-with pub, see [pub.dartlang.org](https://pub.dartlang.org).
+Except for `dart:core`, you must import a library before you can use it. Here's
+an example of importing `dart:html` and `dart:math`:
+
+```dart
+import 'dart:html';
+import 'dart:math';
+```
+
+You can install more libraries using the pub package manager. For information
+on finding, using, and publishing libraries with pub, see
+[pub.dartlang.org](https://pub.dartlang.org).
The main site for learning and using Dart is
-[www.dartlang.org](https://www.dartlang.org).
-Check out these pages:
+[www.dartlang.org](https://www.dartlang.org). Check out these additional pages:
- * [Dart homepage](https://www.dartlang.org)
* [Tutorials](https://www.dartlang.org/docs/tutorials/)
* [Programmer's Guide](https://www.dartlang.org/docs/)
* [Samples](https://www.dartlang.org/samples/)
* [A Tour of the Dart Libraries](https://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html)
This API reference is automatically generated from the source code in the
-[Dart project](https://github.com/dart-lang/sdk).
-If you'd like to contribute to this documentation, see
+[Dart project](https://github.com/dart-lang/sdk). If you'd like to contribute to
+this documentation, see
[Contributing](https://github.com/dart-lang/sdk/wiki/Contributing).
diff --git a/sdk/lib/_internal/js_runtime/lib/annotations.dart b/sdk/lib/_internal/js_runtime/lib/annotations.dart
index 0ade221..ee33493 100644
--- a/sdk/lib/_internal/js_runtime/lib/annotations.dart
+++ b/sdk/lib/_internal/js_runtime/lib/annotations.dart
@@ -51,8 +51,8 @@
/// Annotation that marks the declaration as a patch.
const _Patch patch = const _Patch(null);
-/// Annotation that marks the declaration as a patch for the old emitter.
-const _Patch patch_old = const _Patch('old');
+/// Annotation that marks the declaration as a patch for the full emitter.
+const _Patch patch_full = const _Patch('full');
-/// Annotation that marks the declaration as a patch for the new emitter.
-const _Patch patch_new = const _Patch('new');
+/// Annotation that marks the declaration as a patch for the lazy emitter.
+const _Patch patch_lazy = const _Patch('lazy');
diff --git a/sdk/lib/_internal/js_runtime/lib/core_patch.dart b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
index 4e2e714..2ec85f0 100644
--- a/sdk/lib/_internal/js_runtime/lib/core_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/core_patch.dart
@@ -6,8 +6,8 @@
import "dart:_internal" as _symbol_dev;
import 'dart:_interceptors';
import 'dart:_js_helper' show patch,
- patch_new,
- patch_old,
+ patch_full,
+ patch_lazy,
checkInt,
getRuntimeType,
jsonEncodeNative,
@@ -60,7 +60,7 @@
// Patch for Function implementation.
@patch
class Function {
- @patch_old
+ @patch_full
static apply(Function function,
List positionalArguments,
[Map<Symbol, dynamic> namedArguments]) {
@@ -69,7 +69,7 @@
namedArguments == null ? null : _toMangledNames(namedArguments));
}
- @patch_new
+ @patch_lazy
static apply(Function function,
List positionalArguments,
[Map<Symbol, dynamic> namedArguments]) {
diff --git a/sdk/lib/_internal/js_runtime/lib/isolate_serialization.dart b/sdk/lib/_internal/js_runtime/lib/isolate_serialization.dart
index 9230bba..ba8f66b 100644
--- a/sdk/lib/_internal/js_runtime/lib/isolate_serialization.dart
+++ b/sdk/lib/_internal/js_runtime/lib/isolate_serialization.dart
@@ -61,6 +61,7 @@
if (x is _WorkerSendPort) return serializeWorkerSendPort(x);
if (x is Closure) return serializeClosure(x);
+ if (x is CapabilityImpl) return serializeCapability(x);
return serializeDartObject(x);
}
@@ -204,6 +205,7 @@
case "raw sendport": return deserializeRawSendPort(x);
case "js-object": return deserializeJSObject(x);
case "function": return deserializeClosure(x);
+ case "capability": return deserializeCapability(x);
case "dart": return deserializeDartObject(x);
default: throw "couldn't deserialize: $x";
}
@@ -345,6 +347,12 @@
return result;
}
+ // ['capability', <id>].
+ Capability deserializeCapability(x) {
+ assert(x[0] == 'capability');
+ return new CapabilityImpl._internal(x[1]);
+ }
+
// ['dart', <class-id>, <field-list>].
deserializeDartObject(x) {
assert(x[0] == 'dart');
diff --git a/sdk/lib/_internal/js_runtime/lib/js_number.dart b/sdk/lib/_internal/js_runtime/lib/js_number.dart
index b340247..907da4e 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_number.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_number.dart
@@ -132,7 +132,7 @@
String toStringAsFixed(int fractionDigits) {
checkInt(fractionDigits);
if (fractionDigits < 0 || fractionDigits > 20) {
- throw new RangeError(fractionDigits);
+ throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits");
}
String result = JS('String', r'#.toFixed(#)', this, fractionDigits);
if (this == 0 && isNegative) return "-$result";
@@ -144,7 +144,7 @@
if (fractionDigits != null) {
checkInt(fractionDigits);
if (fractionDigits < 0 || fractionDigits > 20) {
- throw new RangeError(fractionDigits);
+ throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits");
}
result = JS('String', r'#.toExponential(#)', this, fractionDigits);
} else {
@@ -157,7 +157,7 @@
String toStringAsPrecision(int precision) {
checkInt(precision);
if (precision < 1 || precision > 21) {
- throw new RangeError(precision);
+ throw new RangeError.range(precision, 1, 21, "precision");
}
String result = JS('String', r'#.toPrecision(#)',
this, precision);
@@ -390,10 +390,14 @@
// Returns pow(this, e) % m.
int modPow(int e, int m) {
- if (e is! int) throw argumentErrorValue(e);
- if (m is! int) throw argumentErrorValue(m);
- if (e < 0) throw new RangeError(e);
- if (m <= 0) throw new RangeError(m);
+ if (e is! int) {
+ throw new ArgumentError.value(e, "exponent", "not an integer");
+ }
+ if (m is! int) {
+ throw new ArgumentError.value(m, "modulus", "not an integer");
+ }
+ if (e < 0) throw new RangeError.range(e, 0, null, "exponent");
+ if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
if (e == 0) return 1;
int b = this;
if (b < 0 || b > m) {
@@ -412,7 +416,7 @@
// If inv is false, returns gcd(x, y).
// If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1.
- // If inv is true and gcd(x, y) != 1, throws RangeError("Not coprime").
+ // If inv is true and gcd(x, y) != 1, throws Exception("Not coprime").
static int _binaryGcd(int x, int y, bool inv) {
int s = 1;
if (!inv) {
@@ -472,7 +476,7 @@
}
} while (u != 0);
if (!inv) return s*v;
- if (v != 1) throw new RangeError("Not coprime");
+ if (v != 1) throw new Exception("Not coprime");
if (d < 0) {
d += x;
if (d < 0) d += x;
@@ -485,22 +489,29 @@
// Returns 1/this % m, with m > 0.
int modInverse(int m) {
- if (m is! int) throw new ArgumentError(m);
- if (m <= 0) throw new RangeError(m);
+ if (m is! int) {
+ throw new ArgumentError.value(m, "modulus", "not an integer");
+ }
+ if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
if (m == 1) return 0;
int t = this;
if ((t < 0) || (t >= m)) t %= m;
if (t == 1) return 1;
- if ((t == 0) || (t.isEven && m.isEven)) throw new RangeError("Not coprime");
+ if ((t == 0) || (t.isEven && m.isEven)) {
+ throw new Exception("Not coprime");
+ }
return _binaryGcd(m, t, true);
}
- // Returns gcd of abs(this) and abs(other), with this != 0 and other !=0.
+ // Returns gcd of abs(this) and abs(other).
int gcd(int other) {
- if (other is! int) throw new ArgumentError(other);
- if ((this == 0) || (other == 0)) throw new RangeError(0);
+ if (other is! int) {
+ throw new ArgumentError.value(other, "other", "not an integer");
+ }
int x = this.abs();
int y = other.abs();
+ if (x == 0) return y;
+ if (y == 0) return x;
if ((x == 1) || (y == 1)) return 1;
return _binaryGcd(x, y, false);
}
diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart
index 7256445..428e878 100644
--- a/sdk/lib/async/stream.dart
+++ b/sdk/lib/async/stream.dart
@@ -1664,7 +1664,7 @@
}
/**
- * An [Iterable] like interface for the values of a [Stream].
+ * An [Iterator] like interface for the values of a [Stream].
*
* This wraps a [Stream] and a subscription on the stream. It listens
* on the stream, and completes the future returned by [moveNext] when the
@@ -1681,19 +1681,30 @@
/**
* Wait for the next stream value to be available.
*
- * It is not allowed to call this function again until the future has
- * completed. If the returned future completes with anything except `true`,
- * the iterator is done, and no new value will ever be available.
+ * Returns a future which will complete with either `true` or `false`.
+ * Completing with `true` means that another event has been received and
+ * can be read as [current].
+ * Completing with `false` means that the stream itearation is done and
+ * no further events will ever be available.
+ * The future may complete with an error, if the stream produces an error,
+ * which also ends iteration.
*
- * The future may complete with an error, if the stream produces an error.
+ * The function must not be called again until the future returned by a
+ * previous call is completed.
*/
Future<bool> moveNext();
/**
* The current value of the stream.
*
- * Only valid when the future returned by [moveNext] completes with `true`
- * as value, and only until the next call to [moveNext].
+ * Is `null` before the first call to [moveNext] and after a call to
+ * `moveNext` completes with a `false` result or an error.
+ *
+ * When a `moveNext` call completes with `true`, the `current` field holds
+ * the most recent event of the stream, and it stays like that until the next
+ * call to `moveNext`.
+ * Between a call to `moveNext` and when its returned future completes,
+ * the value is unspecified.
*/
T get current;
@@ -1703,13 +1714,14 @@
* The stream iterator is automatically canceled if the [moveNext] future
* completes with either `false` or an error.
*
- * If a [moveNext] call has been made, it will complete with `false` as value,
- * as will all further calls to [moveNext].
- *
* If you need to stop listening for values before the stream iterator is
* automatically closed, you must call [cancel] to ensure that the stream
* is properly closed.
*
+ * If [moveNext] has been called when the iterator is cancelled,
+ * its returned future will complete with `false` as value,
+ * as will all further calls to [moveNext].
+ *
* Returns a future if the cancel-operation is not completed synchronously.
* Otherwise returns `null`.
*/
diff --git a/sdk/lib/core/exceptions.dart b/sdk/lib/core/exceptions.dart
index 38af7af..7484ed5 100644
--- a/sdk/lib/core/exceptions.dart
+++ b/sdk/lib/core/exceptions.dart
@@ -18,15 +18,15 @@
* until the actual exceptions used by a library are done.
*/
abstract class Exception {
- factory Exception([var message]) => new _ExceptionImplementation(message);
+ factory Exception([var message]) => new _Exception(message);
}
/** Default implementation of [Exception] which carries a message. */
-class _ExceptionImplementation implements Exception {
+class _Exception implements Exception {
final message;
- _ExceptionImplementation([this.message]);
+ _Exception([this.message]);
String toString() {
if (message == null) return "Exception";
@@ -174,6 +174,7 @@
}
}
+// Exception thrown when doing integer division with a zero divisor.
class IntegerDivisionByZeroException implements Exception {
const IntegerDivisionByZeroException();
String toString() => "IntegerDivisionByZeroException";
diff --git a/sdk/lib/core/int.dart b/sdk/lib/core/int.dart
index 16a1e07..98f7da8 100644
--- a/sdk/lib/core/int.dart
+++ b/sdk/lib/core/int.dart
@@ -88,7 +88,7 @@
* limit intermediate values by using the "and" operator with a suitable
* mask.
*
- * It is an error of [shiftAmount] is negative.
+ * It is an error if [shiftAmount] is negative.
*/
int operator <<(int shiftAmount);
@@ -99,7 +99,7 @@
* significant bits, effectively doing an integer division by
*`pow(2, shiftIndex)`.
*
- * It is an error of [shiftAmount] is negative.
+ * It is an error if [shiftAmount] is negative.
*/
int operator >>(int shiftAmount);
@@ -116,15 +116,23 @@
* modulo [modulus].
*
* The [modulus] must be positive.
- * Throws if no modular inverse exists.
+ *
+ * It is an error if no modular inverse exists.
*/
int modInverse(int modulus);
/**
- * Returns the greatest common divisor of the absolute value of
- * this integer and the absolute value of [other].
+ * Returns the greatest common divisor of this integer and [other].
*
- * Both this and [other] must be non-zero.
+ * If either number is non-zero, the result is the numerically greatest
+ * integer dividing both `this` and `other`.
+ *
+ * The greatest common divisor is independent of the order,
+ * so `x.gcd(y)` is always the same as `y.gcd(x)`.
+ *
+ * For any integer `x`, `x.gcd(x)` is `x.abs()`.
+ *
+ * If both `this` and `other` is zero, the result is also zero.
*/
int gcd(int other);
@@ -266,27 +274,29 @@
* Converts [this] to a string representation in the given [radix].
*
* In the string representation, lower-case letters are used for digits above
- * '9'.
+ * '9', with 'a' being 10 an 'z' being 35.
*
* The [radix] argument must be an integer in the range 2 to 36.
*/
String toRadixString(int radix);
/**
- * Parse [source] as an integer literal and return its value.
- *
- * The [radix] must be in the range 2..36. The digits used are
- * first the decimal digits 0..9, and then the letters 'a'..'z'.
- * Accepts capital letters as well.
- *
- * If no [radix] is given then it defaults to 10, unless the string starts
- * with "0x", "-0x" or "+0x", in which case the radix is set to 16 and the
- * "0x" is ignored.
+ * Parse [source] as a, possibly signed, integer literal and return its value.
*
* The [source] must be a non-empty sequence of base-[radix] digits,
* optionally prefixed with a minus or plus sign ('-' or '+').
*
- * It must always be the case for an int [:n:] and radix [:r:] that
+ * The [radix] must be in the range 2..36. The digits used are
+ * first the decimal digits 0..9, and then the letters 'a'..'z' with
+ * values 10 through 35. Also accepts upper-case letters with the same
+ * values as the lower-case ones.
+ *
+ * If no [radix] is given then it defaults to 10. In this case, the [source]
+ * digits may also start with `0x`, in which case the number is interpreted
+ * as a hexadecimal literal, which effectively means that the `0x` is ignored
+ * and the radix is instead set to 16.
+ *
+ * For any int [:n:] and radix [:r:], it is guaranteed that
* [:n == int.parse(n.toRadixString(r), radix: r):].
*
* If the [source] is not a valid integer literal, optionally prefixed by a
diff --git a/tests/benchmark_smoke/benchmark_smoke.status b/tests/benchmark_smoke/benchmark_smoke.status
index fea516e0..d3fe9a3 100644
--- a/tests/benchmark_smoke/benchmark_smoke.status
+++ b/tests/benchmark_smoke/benchmark_smoke.status
@@ -8,4 +8,3 @@
[ $compiler == dart2js && $runtime == none ]
*: Fail, Pass # TODO(ahe): Triage these tests.
-[ $compiler == dart2js && $cps_ir ]
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index de69117..97a467e 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -2649,7 +2649,6 @@
LayoutTests/fast/table/border-changes_t01: RuntimeError # Please triage this failure
LayoutTests/fast/table/caption-orthogonal-writing-mode-sizing_t01: RuntimeError # Please triage this failure
LayoutTests/fast/table/css-table-max-width_t01: RuntimeError # Please triage this failure
-LayoutTests/fast/table/incorrect-colgroup-span-values_t01: RuntimeError # Please triage this failure
LayoutTests/fast/table/large-shrink-wrapped-width_t01: RuntimeError # Please triage this failure
LayoutTests/fast/table/min-width-css-block-table_t01: RuntimeError # Please triage this failure
LayoutTests/fast/table/min-width-css-inline-table_t01: RuntimeError # Please triage this failure
@@ -3124,6 +3123,7 @@
LayoutTests/fast/css3-text/css3-text-decoration/getComputedStyle/getComputedStyle-text-decoration-style_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/MutationObserver/observe-options-attributes_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/MutationObserver/observe-options-character-data_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/table/incorrect-colgroup-span-values_t01: RuntimeError # Please triage this failure
LayoutTests/fast/xmlhttprequest/xmlhttprequest-responsetype-before-open_t01: RuntimeError # Please triage this failure
LibTest/html/HttpRequest/responseType_A01_t02: RuntimeError # Please triage this failure
@@ -9602,236 +9602,189 @@
WebPlatformTest/webstorage/storage_session_setitem_t01: RuntimeError # Please triage this failure
[ $compiler == dart2js && $cps_ir ]
-Language/12_Expressions/05_Strings/1_String_Interpolation_A01_t12: RuntimeError # Cannot read property 'prototype' of undefined
-Language/12_Expressions/05_Strings/1_String_Interpolation_A02_t02: RuntimeError # Cannot read property 'prototype' of undefined
Language/12_Expressions/12_Instance_Creation/1_New_A06_t12: RuntimeError # Please triage this failure.
-Language/12_Expressions/13_Property_Extraction_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-Language/12_Expressions/13_Property_Extraction_A01_t02: RuntimeError # Cannot read property 'prototype' of undefined
-Language/12_Expressions/13_Property_Extraction_A01_t03: RuntimeError # Cannot read property 'prototype' of undefined
Language/12_Expressions/13_Property_Extraction_A03_t01: RuntimeError # Cannot read property 'call' of undefined
Language/12_Expressions/13_Property_Extraction_A03_t02: RuntimeError # Cannot read property 'call' of undefined
Language/12_Expressions/13_Property_Extraction_A03_t03: RuntimeError # Cannot read property 'call' of undefined
Language/12_Expressions/13_Spawning_an_Isolate_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-Language/12_Expressions/15_Method_Invocation/1_Ordinary_Invocation_A05_t04: RuntimeError # Cannot read property 'prototype' of undefined
-Language/12_Expressions/15_Method_Invocation/3_Static_Invocation_A04_t05: RuntimeError # Cannot read property 'prototype' of undefined
Language/12_Expressions/15_Method_Invocation/4_Super_Invocation_A01_t01: RuntimeError # Cannot read property 'call' of undefined
-Language/12_Expressions/15_Method_Invocation/4_Super_Invocation_A03_t04: RuntimeError # Cannot read property 'prototype' of undefined
-Language/12_Expressions/29_Assignable_Expressions_A01_t08: RuntimeError # receiver.get$_first is not a function
Language/12_Expressions/30_Identifier_Reference_A09_t03: Crash # (i=0): For-loop variable captured in loop header
-Language/12_Expressions/30_Identifier_Reference_A10_t01: RuntimeError # Cannot read property 'prototype' of undefined
-Language/12_Expressions/30_Identifier_Reference_A10_t02: RuntimeError # Cannot read property 'prototype' of undefined
-Language/13_Statements/02_Expression_Statements_A01_t06: RuntimeError # Cannot read property 'prototype' of undefined
Language/13_Statements/06_For_A01_t07: Crash # unsupported operation on erroneous element
Language/13_Statements/12_Labels_A03_t04: Crash # (switch (i){L:case 0:flag=true;break;case 2:continue L;}): continue to a labeled switch case
Language/13_Statements/14_Continue_A02_t12: Crash # (switch (2){L:case 1:flag=true;break;case 2:continue L;}): continue to a labeled switch case
Language/13_Statements/14_Continue_A02_t13: Crash # (switch (2){case 2:continue L;L:case 1:flag=true;}): continue to a labeled switch case
-Language/14_Libraries_and_Scripts/4_Scripts_A03_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-Language/15_Types/3_Type_Declarations/1_Typedef_A02_t01: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/3_Type_Declarations/1_Typedef_A02_t02: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/3_Type_Declarations/1_Typedef_A02_t03: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/3_Type_Declarations/1_Typedef_A03_t01: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/3_Type_Declarations/1_Typedef_A04_t02: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/4_Interface_Types_A12_t18: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t03: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t04: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t05: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t06: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t08: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t09: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A01_t11: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A02_t01: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A02_t02: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A02_t03: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A02_t04: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A02_t05: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A02_t09: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A02_t10: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t01: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t02: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t03: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t04: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t06: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t08: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t09: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t10: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t11: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t12: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A03_t13: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A05_t02: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A05_t05: RuntimeError # Cannot read property 'prototype' of undefined
-Language/15_Types/5_Function_Types_A06_t01: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/async/Completer/Completer.sync_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/completeError_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/completeError_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/completeError_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/completeError_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/completeError_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/completeError_A03_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A01_t05: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A01_t06: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/complete_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Completer/isCompleted_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/DeferredLibrary/DeferredLibrary_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.delayed_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.delayed_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.delayed_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.error_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.microtask_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.microtask_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.microtask_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.microtask_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.sync_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.sync_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.sync_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.sync_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.value_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future.value_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/Future_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/asStream_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/asStream_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/asStream_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/asStream_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/catchError_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/catchError_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/catchError_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/catchError_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+Language/14_Libraries_and_Scripts/4_Scripts_A03_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/Completer.sync_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/completeError_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/completeError_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/completeError_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/completeError_A01_t04: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/completeError_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/completeError_A03_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A01_t04: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A01_t05: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A01_t06: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/complete_A02_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Completer/isCompleted_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/DeferredLibrary/DeferredLibrary_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.delayed_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.delayed_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.delayed_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.error_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.microtask_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.microtask_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.microtask_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.microtask_A01_t04: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.sync_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.sync_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.sync_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.sync_A01_t04: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.value_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future.value_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/Future_A01_t04: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/asStream_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/asStream_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/asStream_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/asStream_A02_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/catchError_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/catchError_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/catchError_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/catchError_A03_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Future/catchError_A03_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/catchError_A03_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/catchError_A03_t04: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Future/catchError_A03_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/catchError_A03_t04: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Future/catchError_A03_t05: RuntimeError # receiver.get$_nums is not a function
-LibTest/async/Future/forEach_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/forEach_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Future/forEach_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/forEach_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Future/forEach_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A03_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Future/then_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A01_t04: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A03_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A04_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Future/then_A05_t01: RuntimeError # receiver.get$_nums is not a function
-LibTest/async/Future/then_A05_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/then_A05_t03: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Future/then_A05_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/then_A05_t03: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Future/wait_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Future/wait_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Future/wait_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Future/wait_A01_t05: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Future/wait_A01_t06: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Future/wait_A01_t07: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/wait_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/wait_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/whenComplete_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/whenComplete_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/whenComplete_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/whenComplete_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Future/whenComplete_A04_t02: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Future/wait_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/wait_A02_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/whenComplete_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/whenComplete_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/whenComplete_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/whenComplete_A04_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Future/whenComplete_A04_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/Stream.eventTransformed_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Stream/Stream.eventTransformed_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.fromFuture_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.fromFuture_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.fromFuture_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.fromIterable_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.fromIterable_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.fromIterable_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.periodic_A01_t01 : RuntimeError # TypeError: receiver.get$_nums is not a function
-LibTest/async/Stream/Stream.periodic_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream.periodic_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/Stream_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/any_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/any_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/any_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/Stream.fromFuture_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.fromFuture_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.fromFuture_A02_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.fromIterable_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.fromIterable_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.fromIterable_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.periodic_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.periodic_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream.periodic_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/Stream_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/any_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/any_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/any_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/asBroadcastStream_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/asBroadcastStream_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/asBroadcastStream_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/asBroadcastStream_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/asBroadcastStream_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/asBroadcastStream_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/asBroadcastStream_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/asBroadcastStream_A01_t04: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/asBroadcastStream_A03_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/asBroadcastStream_A03_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/asBroadcastStream_A03_t03: RuntimeError # receiver.get$_nums is not a function
-LibTest/async/Stream/asBroadcastStream_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/asBroadcastStream_A04_t02: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/asBroadcastStream_A04_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/asBroadcastStream_A04_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/asBroadcastStream_A04_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/contains_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/contains_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/contains_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/distinct_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/distinct_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/drain_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/drain_A02_t01 : RuntimeError # TypeError: receiver.get$_nums is not a function
-LibTest/async/Stream/drain_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/elementAt_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/elementAt_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/elementAt_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/elementAt_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/every_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/every_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/contains_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/contains_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/contains_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/distinct_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/distinct_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/drain_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/drain_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/drain_A02_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/elementAt_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/elementAt_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/elementAt_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/elementAt_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/every_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/every_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/expand_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/firstWhere_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/firstWhere_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/firstWhere_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/firstWhere_A03_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/first_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/first_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/first_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/first_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/fold_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/fold_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/firstWhere_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/firstWhere_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/firstWhere_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/firstWhere_A03_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/first_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/first_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/first_A02_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/first_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/fold_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/fold_A01_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/forEach_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/forEach_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/forEach_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/forEach_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Stream/handleError_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Stream/handleError_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Stream/handleError_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/handleError_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/handleError_A04_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/handleError_A04_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/isBroadcast_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/isBroadcast_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/isEmpty_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/join_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/join_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/join_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/join_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/lastWhere_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/lastWhere_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/lastWhere_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/lastWhere_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/last_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/last_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/last_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/length_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/handleError_A04_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/handleError_A04_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/handleError_A04_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/isBroadcast_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/isBroadcast_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/isEmpty_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/join_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/join_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/join_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/join_A02_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/lastWhere_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/lastWhere_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/lastWhere_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/lastWhere_A04_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/last_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/last_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/last_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/length_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/listen_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/listen_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/listen_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/listen_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/listen_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/listen_A05_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/listen_A05_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/listen_A05_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/listen_A06_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/listen_A04_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/listen_A05_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/listen_A05_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/listen_A05_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/listen_A06_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/map_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Stream/pipe_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/reduce_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/reduce_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/reduce_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/singleWhere_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/singleWhere_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/single_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/single_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/skipWhile_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/skip_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/takeWhile_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/take_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/take_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Stream/take_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Stream/reduce_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/reduce_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/reduce_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/singleWhere_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/singleWhere_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/single_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/single_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/skipWhile_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/skip_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/takeWhile_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/take_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/take_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Stream/take_A01_t03: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/toList_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/Stream/toSet_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/transform_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
@@ -9839,77 +9792,77 @@
LibTest/async/Stream/where_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Stream/where_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamController/StreamController.broadcast_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController.broadcast_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController.broadcast_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController.broadcast_A05_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController.broadcast_A06_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController.broadcast_A07_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController.broadcast_A07_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController.broadcast_A08_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamController/StreamController.broadcast_A03_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController.broadcast_A04_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController.broadcast_A05_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController.broadcast_A06_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController.broadcast_A07_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController.broadcast_A07_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController.broadcast_A08_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamController/StreamController_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController_A04_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController_A05_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController_A06_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/StreamController_A06_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/addError_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/addError_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamController/StreamController_A04_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController_A05_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController_A06_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/StreamController_A06_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/addError_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/addError_A01_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamController/addStream_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamController/addStream_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamController/addStream_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamController/addStream_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamController/addStream_A03_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/add_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/close_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/close_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/done_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/done_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/done_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/hasListener_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/hasListener_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/isClosed_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/isClosed_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/isPaused_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/isPaused_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/isPaused_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/sink_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamController/stream_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamIterator/StreamIterator_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamController/add_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/close_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/close_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/done_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/done_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/done_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/hasListener_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/hasListener_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/isClosed_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/isClosed_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/isPaused_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/isPaused_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/isPaused_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/sink_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamController/stream_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamIterator/StreamIterator_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamIterator/cancel_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamIterator/current_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamIterator/moveNext_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamIterator/moveNext_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamSink/addError_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamIterator/current_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamIterator/moveNext_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamIterator/moveNext_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamSink/addError_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamSink/addStream_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamSink/addStream_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamSink/addStream_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamSink/addStream_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamSink/addStream_A01_t04: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamSink/addStream_A01_t05: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamSink/add_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamSink/close_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamSink/done_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer.fromHandlers_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer.fromHandlers_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer.fromHandlers_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer.fromHandlers_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer.fromHandlers_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer.fromHandlers_A01_t03: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamTransformer/StreamTransformer.fromHandlers_A01_t04: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamTransformer/StreamTransformer_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/StreamTransformer/StreamTransformer_A03_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/StreamTransformer/StreamTransformer_A03_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/StreamTransformer/StreamTransformer_A03_t02: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/async/StreamTransformer/bind_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/Timer.periodic_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/Timer.periodic_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/Timer_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/Timer_A02_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/cancel_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/isActive_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/isActive_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/run_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/async/Timer/run_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Timer/Timer.periodic_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/Timer.periodic_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/Timer_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/Timer_A02_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/cancel_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/isActive_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/isActive_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/run_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/async/Timer/run_A01_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Zone/bindBinaryCallback_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Zone/bindBinaryCallback_A01_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Zone/bindCallback_A01_t01: RuntimeError # receiver.get$_nums is not a function
@@ -9941,55 +9894,41 @@
LibTest/async/Zone/runUnaryGuarded_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Zone/runUnary_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Zone/run_A01_t01: RuntimeError # receiver.get$_nums is not a function
-LibTest/async/Zone/scheduleMicrotask_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/async/Zone/scheduleMicrotask_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/async/Zone/scheduleMicrotask_A01_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/collection/ListBase/ListBase_class_A01_t02: Crash # Stack Overflow
LibTest/collection/ListMixin/ListMixin_class_A01_t02: Crash # Stack Overflow
-LibTest/convert/JsonCodec/encode_A01_t01: Crash # Internal Error: No default constructor available.
-LibTest/convert/JsonCodec/encode_A01_t02: Crash # Internal Error: No default constructor available.
-LibTest/convert/JsonDecoder/fuse_A01_t01: Crash # Internal Error: No default constructor available.
-LibTest/convert/JsonEncoder/convert_A01_t01: Crash # Internal Error: No default constructor available.
-LibTest/core/DateTime/parse_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/core/DateTime/parse_A01_t02: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/core/DateTime/parse_A03_t01: Pass # Please triage this failure.
-LibTest/core/DateTime/timeZoneName_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
LibTest/core/Invocation/isAccessor_A01_t01: Crash # Class 'PartialMethodElement' has no instance getter 'initializer'.
-LibTest/core/Invocation/isGetter_A01_t01 : RuntimeError #
-LibTest/core/Invocation/isGetter_A01_t02 : RuntimeError #
+LibTest/core/Invocation/isGetter_A01_t01: RuntimeError # Please triage this failure.
+LibTest/core/Invocation/isGetter_A01_t02: RuntimeError # Please triage this failure.
LibTest/core/Invocation/isMethod_A01_t02: Crash # Class 'PartialMethodElement' has no instance getter 'initializer'.
LibTest/core/Invocation/isSetter_A01_t01: Crash # Class 'PartialMethodElement' has no instance getter 'initializer'.
-LibTest/core/Invocation/isSetter_A01_t02 : RuntimeError #
+LibTest/core/Invocation/isSetter_A01_t02: RuntimeError # Please triage this failure.
LibTest/core/Invocation/memberName_A01_t01: Crash # Class 'PartialMethodElement' has no instance getter 'initializer'.
-LibTest/core/Invocation/positionalArguments_A01_t01 : RuntimeError #
+LibTest/core/Invocation/namedArguments_A01_t01: RuntimeError # Please triage this failure.
+LibTest/core/Invocation/positionalArguments_A01_t01: RuntimeError # Please triage this failure.
LibTest/core/List/List_class_A01_t02: Crash # Stack Overflow
-LibTest/core/Match/end_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/core/Match/groupCount_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/core/NoSuchMethodError/toString_A01_t01: RuntimeError # receiver.get$_first is not a function
-LibTest/core/RegExp/stringMatch_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/core/Stopwatch/Stopwatch_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/elapsedTicks_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/frequency_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
-LibTest/core/Stopwatch/start_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/start_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/start_A01_t03: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/elapsedTicks_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/start_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/start_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/start_A01_t03: RuntimeError # receiver.get$_nums is not a function
+LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/core/Symbol/Symbol_A01_t03: RuntimeError # Please triage this failure.
LibTest/core/Symbol/Symbol_A01_t05: RuntimeError # Please triage this failure.
LibTest/core/double/INFINITY_A01_t04: Pass # Please triage this failure.
LibTest/core/double/NEGATIVE_INFINITY_A01_t04: Pass # Please triage this failure.
-LibTest/core/int/parse_A01_t01: RuntimeError # Cannot read property 'prototype' of undefined
LibTest/isolate/Isolate/spawnUri_A02_t02: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/isolate/Isolate/spawnUri_A02_t03: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/isolate/Isolate/spawnUri_A02_t04: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/isolate/Isolate/spawn_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/isolate/Isolate/spawn_A01_t02: RuntimeError # receiver.get$_nums is not a function
+LibTest/isolate/Isolate/spawn_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
LibTest/isolate/Isolate/spawn_A01_t03: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/Isolate/spawn_A01_t04: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/Isolate/spawn_A01_t05: RuntimeError # receiver.get$_nums is not a function
@@ -10019,7 +9958,7 @@
LibTest/isolate/ReceivePort/contains_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/distinct_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/distinct_A01_t02: RuntimeError # receiver.get$_nums is not a function
-LibTest/isolate/ReceivePort/drain_A02_t01 : RuntimeError # TypeError: receiver.get$_nums is not a function
+LibTest/isolate/ReceivePort/drain_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/drain_A02_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/elementAt_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/elementAt_A03_t01: RuntimeError # receiver.get$_nums is not a function
@@ -10034,12 +9973,12 @@
LibTest/isolate/ReceivePort/fold_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/fold_A01_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/forEach_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/isolate/ReceivePort/isBroadcast_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
-LibTest/isolate/ReceivePort/isBroadcast_A01_t02: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/isolate/ReceivePort/isBroadcast_A01_t01: RuntimeError # receiver.get$_nums is not a function
+LibTest/isolate/ReceivePort/isBroadcast_A01_t02: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/isEmpty_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/join_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/join_A01_t02: RuntimeError # receiver.get$_nums is not a function
-LibTest/isolate/ReceivePort/lastWhere_A01_t01: RuntimeError # receiver.get$_collection$_nums is not a function
+LibTest/isolate/ReceivePort/lastWhere_A01_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/lastWhere_A02_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/lastWhere_A04_t01: RuntimeError # receiver.get$_nums is not a function
LibTest/isolate/ReceivePort/last_A01_t01: RuntimeError # receiver.get$_nums is not a function
@@ -10098,4 +10037,3 @@
LibTest/typed_data/Uint8List/setAll_A01_t01: RuntimeError # this.get$length is not a function
LibTest/typed_data/Uint8List/setRange_A01_t01: RuntimeError # this.get$length is not a function
LibTest/typed_data/Uint8List/setRange_A02_t01: RuntimeError # this.get$length is not a function
-LibTest/core/Invocation/namedArguments_A01_t01 : RuntimeError #
diff --git a/tests/compiler/dart2js/analyze_unused_dart2js_test.dart b/tests/compiler/dart2js/analyze_unused_dart2js_test.dart
index 07f049c..db3923a 100644
--- a/tests/compiler/dart2js/analyze_unused_dart2js_test.dart
+++ b/tests/compiler/dart2js/analyze_unused_dart2js_test.dart
@@ -38,6 +38,10 @@
"lib/src/constants/expressions.dart": const [
"The method 'readFromEnvironment' is never called"],
+ // Serialization code is only used in test.
+ "lib/src/serialization/": const [
+ "is never"],
+
// Nested functions are currently kept alive in the IR.
"lib/src/tree_ir/": const [
"accept", "FunctionExpression", "CreateFunction"
diff --git a/tests/compiler/dart2js/dart2js.status b/tests/compiler/dart2js/dart2js.status
index fa809e3..82511ce 100644
--- a/tests/compiler/dart2js/dart2js.status
+++ b/tests/compiler/dart2js/dart2js.status
@@ -42,6 +42,10 @@
backend_dart/opt_shrinking_test: Skip
backend_dart/opt_redundant_phi_test: Skip
+# These tests run the compiler multiple times.
+js_backend_cps_ir_basic_test: Pass, Slow
+js_backend_cps_ir_closures_test: Pass, Slow
+
[ $unchecked ]
exit_code_test: Skip # This tests requires checked mode.
@@ -70,8 +74,6 @@
exit_code_test: Pass, Slow
import_mirrors_test: Pass, Slow
in_user_code_test: Pass, Slow
-js_backend_cps_ir_basic_test: Pass, Slow
-js_backend_cps_ir_closures_test: Pass, Slow
message_kind_test: Pass, Slow
show_package_warnings_test: Pass, Slow
source_map_pub_build_validity_test: Pass, Slow
diff --git a/tests/compiler/dart2js/js_backend_cps_ir_closures_test.dart b/tests/compiler/dart2js/js_backend_cps_ir_closures_test.dart
index 97b53e7..42d4e5a 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_closures_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_closures_test.dart
@@ -106,6 +106,86 @@
function() {
return new V.A_b_closure(this);
}"""),
+
+ const TestEntry("""
+staticMethod(x) => x;
+main(x) {
+ var tearOff = staticMethod;
+ print(tearOff(123));
+}
+""",
+r"""
+function(x) {
+ P.print(V.staticMethod(123));
+}"""),
+
+ const TestEntry("""
+class Foo {
+ instanceMethod(x) => x;
+}
+main(x) {
+ var tearOff = new Foo().instanceMethod;
+ print(tearOff(123));
+}
+""",
+r"""
+function(x) {
+ P.print(V.Foo$().instanceMethod$1(123));
+}"""),
+
+ const TestEntry("""
+class Foo {
+ instanceMethod(x) => x;
+}
+main(x) {
+ var tearOff = new Foo().instanceMethod;
+ print(tearOff(123));
+ print(tearOff(321));
+}
+""",
+r"""
+function(x) {
+ var v0 = V.Foo$();
+ P.print(v0.instanceMethod$1(123));
+ P.print(v0.instanceMethod$1(321));
+}"""),
+
+ const TestEntry("""
+class Foo {
+ get getter {
+ print('getter');
+ return (x) => x;
+ }
+}
+main(x) {
+ var notTearOff = new Foo().getter;
+ print(notTearOff(123));
+ print(notTearOff(321));
+}
+""",
+r"""
+function(x) {
+ var notTearOff = V.Foo$().get$getter();
+ P.print(notTearOff.call$1(123));
+ P.print(notTearOff.call$1(321));
+}"""),
+
+ const TestEntry("""
+class Foo {
+ get getter {
+ print('getter');
+ return (x) => x;
+ }
+}
+main(x) {
+ var notTearOff = new Foo().getter;
+ print(notTearOff(123));
+}
+""",
+r"""
+function(x) {
+ P.print(V.Foo$().getter$1(123));
+}"""),
];
void main() {
diff --git a/tests/compiler/dart2js/js_backend_cps_ir_control_flow_test.dart b/tests/compiler/dart2js/js_backend_cps_ir_control_flow_test.dart
index b6fc9ad..b16a1a8 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_control_flow_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_control_flow_test.dart
@@ -57,11 +57,11 @@
}""", """
function() {
var i = 0;
- L1:
+ L2:
while (P.identical(V.foo(true), true)) {
P.print(1);
if (P.identical(V.foo(false), true))
- break L1;
+ break L2;
i = V.foo(i);
}
P.print(2);
diff --git a/tests/compiler/dart2js/memory_source_file_helper.dart b/tests/compiler/dart2js/memory_source_file_helper.dart
index 4efb8ad..0a0428b 100644
--- a/tests/compiler/dart2js/memory_source_file_helper.dart
+++ b/tests/compiler/dart2js/memory_source_file_helper.dart
@@ -14,7 +14,7 @@
show currentDirectory;
import 'package:compiler/src/io/source_file.dart'
- show StringSourceFile;
+ show StringSourceFile, SourceFile;
import 'package:compiler/src/source_file_provider.dart'
show SourceFileProvider;
@@ -41,4 +41,15 @@
}
Future<String> call(Uri resourceUri) => readStringFromUri(resourceUri);
+
+ SourceFile getSourceFile(Uri resourceUri) {
+ if (resourceUri.scheme != 'memory') {
+ return super.getSourceFile(resourceUri);
+ }
+ String source = memorySourceFiles[resourceUri.path];
+ if (source == null) {
+ throw new Exception('No such file $resourceUri');
+ }
+ return new StringSourceFile.fromUri(resourceUri, source);
+ }
}
diff --git a/tests/compiler/dart2js/mock_libraries.dart b/tests/compiler/dart2js/mock_libraries.dart
index 296e18b..a9f96ef 100644
--- a/tests/compiler/dart2js/mock_libraries.dart
+++ b/tests/compiler/dart2js/mock_libraries.dart
@@ -194,8 +194,8 @@
'numTypeCheck': 'numTypeCheck(value) {}',
'_Patch': 'class _Patch { final tag; const _Patch(this.tag); }',
'patch': 'const patch = const _Patch(null);',
- 'patch_new': 'const patch_new = const _Patch("new");',
- 'patch_old': 'const patch_old = const _Patch("old");',
+ 'patch_full': 'const patch_full = const _Patch("full");',
+ 'patch_lazy': 'const patch_lazy = const _Patch("lazy");',
'propertyTypeCast': 'propertyTypeCast(x) {}',
'propertyTypeCheck': 'propertyTypeCheck(value, property) {}',
'requiresPreamble': 'requiresPreamble() {}',
diff --git a/tests/compiler/dart2js/output_collector.dart b/tests/compiler/dart2js/output_collector.dart
index c6bc101..d2d3af4 100644
--- a/tests/compiler/dart2js/output_collector.dart
+++ b/tests/compiler/dart2js/output_collector.dart
@@ -26,6 +26,29 @@
}
}
+class CloningEventSink implements EventSink<String> {
+ final List<EventSink<String>> sinks;
+
+ CloningEventSink(this.sinks);
+
+ @override
+ void add(String event) {
+ sinks.forEach((EventSink<String> sink) => sink.add(event));
+ }
+
+ @override
+ void addError(errorEvent, [StackTrace stackTrace]) {
+ sinks.forEach((EventSink<String> sink) {
+ sink.addError(errorEvent, stackTrace);
+ });
+ }
+
+ @override
+ void close() {
+ sinks.forEach((EventSink<String> sink) => sink.close());
+ }
+}
+
class OutputCollector {
Map<String, Map<String, BufferedEventSink>> outputMap = {};
diff --git a/tests/compiler/dart2js/patch_test.dart b/tests/compiler/dart2js/patch_test.dart
index 6f59878..c198012 100644
--- a/tests/compiler/dart2js/patch_test.dart
+++ b/tests/compiler/dart2js/patch_test.dart
@@ -136,13 +136,13 @@
testPatchVersioned() {
- String oldPatch = "test(){return 'string';}";
- String newPatch = "test(){return 'new and improved string';}";
+ String fullPatch = "test(){return 'string';}";
+ String lazyPatch = "test(){return 'new and improved string';}";
String patchSource =
"""
- @patch_old $oldPatch
- @patch_new $newPatch
+ @patch_full $fullPatch
+ @patch_lazy $lazyPatch
""";
test(String patchVersion,
@@ -192,11 +192,11 @@
}));
}
- test('old', patchText: oldPatch);
- test('new', patchText: newPatch);
+ test('full', patchText: fullPatch);
+ test('lazy', patchText: lazyPatch);
test('unknown', expectIsPatched: false,
expectedError: 'External method without an implementation.');
- test('old',
+ test('full',
defaultPatch: "@patch test(){}",
expectedInternalError: "Trying to patch a function more than once.");
}
diff --git a/tests/compiler/dart2js/serialization_analysis_test.dart b/tests/compiler/dart2js/serialization_analysis_test.dart
new file mode 100644
index 0000000..775f6741
--- /dev/null
+++ b/tests/compiler/dart2js/serialization_analysis_test.dart
@@ -0,0 +1,237 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization_analysis_test;
+
+import 'dart:async';
+import 'package:async_helper/async_helper.dart';
+import 'package:expect/expect.dart';
+import 'package:compiler/src/elements/elements.dart';
+import 'package:compiler/src/serialization/serialization.dart';
+import 'package:compiler/src/serialization/json_serializer.dart';
+import 'package:compiler/src/serialization/task.dart';
+import 'package:compiler/src/dart2jslib.dart';
+import 'package:compiler/src/filenames.dart';
+import 'memory_compiler.dart';
+
+const List<Test> TESTS = const <Test>[
+ const Test(const {
+ 'main.dart': 'main() => print("Hello World");'
+ }),
+
+ const Test(const {
+ 'main.dart': 'main() => print("Hello World", 0);'
+ },
+ expectedWarningCount: 1,
+ expectedInfoCount: 1),
+
+ const Test(const {
+ 'main.dart': r'''
+main() {
+ String text = "Hello World";
+ print('$text');
+}'''
+ }),
+
+ const Test(const {
+ 'main.dart': r'''
+main() {
+ String text = "Hello World";
+ print('$text', text);
+}'''
+ },
+ expectedWarningCount: 1,
+ expectedInfoCount: 1),
+
+ const Test(const {
+ 'main.dart': r'''
+main(List<String> arguments) {
+ print(arguments);
+}'''
+ }),
+
+ const Test(const {
+ 'main.dart': r'''
+main(List<String> arguments) {
+ for (int i = 0; i < arguments.length; i++) {
+ print(arguments[i]);
+ }
+}'''
+ }),
+
+ const Test(const {
+ 'main.dart': r'''
+main(List<String> arguments) {
+ for (String argument in arguments) {
+ print(argument);
+ }
+}'''
+ }),
+
+ const Test(const {
+ 'main.dart': r'''
+class Class {}
+main() {
+ print(new Class());
+}'''
+ }),
+
+ const Test(const {
+ 'main.dart': r'''
+class Class implements Function {}
+main() {
+ print(new Class());
+}'''
+ },
+ expectedWarningCount: 1),
+
+ const Test(const {
+ 'main.dart': r'''
+class Class implements Function {
+ call() {}
+}
+main() {
+ print(new Class()());
+}'''
+ }),
+
+ const Test(const {
+ 'main.dart': r'''
+class Class implements Comparable<Class> {
+ int compareTo(Class other) => 0;
+}
+main() {
+ print(new Class());
+}'''
+ }),
+
+ const Test(const {
+ 'main.dart': r'''
+class Class implements Comparable<Class, Class> {
+ int compareTo(other) => 0;
+}
+main() {
+ print(new Class());
+}'''
+ },
+ expectedWarningCount: 1),
+
+ const Test(const {
+ 'main.dart': r'''
+class Class implements Comparable<Class> {
+ int compareTo(String other) => 0;
+}
+main() {
+ print(new Class().compareTo(null));
+}'''
+ },
+ expectedWarningCount: 1,
+ expectedInfoCount: 1),
+
+ const Test(const {
+ 'main.dart': r'''
+class Class implements Comparable {
+ bool compareTo(a, b) => true;
+}
+main() {
+ print(new Class().compareTo(null, null));
+}'''
+ },
+ expectedWarningCount: 1,
+ expectedInfoCount: 1),
+];
+
+main(List<String> arguments) {
+ asyncTest(() async {
+ String serializedData = await serializeDartCore();
+
+ if (arguments.isNotEmpty) {
+ Uri entryPoint = Uri.base.resolve(nativeToUriPath(arguments.last));
+ await analyze(serializedData, entryPoint, null);
+ } else {
+ Uri entryPoint = Uri.parse('memory:main.dart');
+ for (Test test in TESTS) {
+ await analyze(serializedData, entryPoint, test);
+ }
+ }
+ });
+}
+
+class Test {
+ final Map sourceFiles;
+ final int expectedErrorCount;
+ final int expectedWarningCount;
+ final int expectedHintCount;
+ final int expectedInfoCount;
+
+ const Test(this.sourceFiles, {
+ this.expectedErrorCount: 0,
+ this.expectedWarningCount: 0,
+ this.expectedHintCount: 0,
+ this.expectedInfoCount: 0});
+}
+
+Future analyze(String serializedData, Uri entryPoint, Test test) async {
+ Deserializer deserializer = new Deserializer.fromText(
+ serializedData, const JsonSerializationDecoder());
+ DiagnosticCollector diagnosticCollector = new DiagnosticCollector();
+ Compiler compiler = compilerFor(
+ test != null ? test.sourceFiles : const {},
+ options: ['--analyze-only', '--output-type=dart'],
+ diagnosticHandler: diagnosticCollector);
+ compiler.serialization.deserializer = new _DeserializerSystem(deserializer);
+ await compiler.runCompiler(entryPoint);
+ if (test != null) {
+ Expect.equals(test.expectedErrorCount, diagnosticCollector.errors.length,
+ "Unexpected error count.");
+ Expect.equals(
+ test.expectedWarningCount,
+ diagnosticCollector.warnings.length,
+ "Unexpected warning count.");
+ Expect.equals(test.expectedHintCount, diagnosticCollector.hints.length,
+ "Unexpected hint count.");
+ Expect.equals(test.expectedInfoCount, diagnosticCollector.infos.length,
+ "Unexpected info count.");
+ }
+}
+
+Future<String> serializeDartCore() async {
+ Compiler compiler = compilerFor({},
+ options: ['--analyze-all', '--output-type=dart']);
+ await compiler.runCompiler(Uri.parse('dart:core'));
+ return serialize(compiler.libraryLoader.libraries);
+}
+
+String serialize(Iterable<LibraryElement> libraries) {
+ Serializer serializer = new Serializer(const JsonSerializationEncoder());
+ for (LibraryElement library in libraries) {
+ serializer.serialize(library);
+ }
+ return serializer.toText();
+}
+
+class _DeserializerSystem extends DeserializerSystem {
+ final Deserializer _deserializer;
+ final List<LibraryElement> deserializedLibraries = <LibraryElement>[];
+
+ _DeserializerSystem(this._deserializer);
+
+ LibraryElement readLibrary(Uri resolvedUri) {
+ LibraryElement library = _deserializer.lookupLibrary(resolvedUri);
+ if (library != null) {
+ deserializedLibraries.add(library);
+ }
+ return library;
+ }
+
+ @override
+ WorldImpact computeWorldImpact(Element element) {
+ return const WorldImpact();
+ }
+
+ @override
+ bool isDeserialized(Element element) {
+ return deserializedLibraries.contains(element.library);
+ }
+}
\ No newline at end of file
diff --git a/tests/compiler/dart2js/serialization_test.dart b/tests/compiler/dart2js/serialization_test.dart
new file mode 100644
index 0000000..a258c2d
--- /dev/null
+++ b/tests/compiler/dart2js/serialization_test.dart
@@ -0,0 +1,1049 @@
+// Copyright (c) 2015, 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.
+
+library dart2js.serialization_test;
+
+import 'dart:io';
+import 'memory_compiler.dart';
+import 'package:async_helper/async_helper.dart';
+import 'package:compiler/src/constants/expressions.dart';
+import 'package:compiler/src/dart_types.dart';
+import 'package:compiler/src/dart2jslib.dart';
+import 'package:compiler/src/elements/elements.dart';
+import 'package:compiler/src/elements/visitor.dart';
+import 'package:compiler/src/ordered_typeset.dart';
+import 'package:compiler/src/serialization/serialization.dart';
+import 'package:compiler/src/serialization/json_serializer.dart';
+import 'package:compiler/src/tree/tree.dart';
+
+main(List<String> arguments) {
+ // Ensure that we can print out constant expressions.
+ DEBUG_MODE = true;
+
+ Uri entryPoint;
+ String outPath;
+ bool prettyPrint = false;
+ for (String arg in arguments) {
+ if (arg.startsWith('--')) {
+ if (arg.startsWith('--out=')) {
+ outPath = arg.substring('--out='.length);
+ } else if (arg == '--pretty-print') {
+ prettyPrint = true;
+ } else {
+ print("Unknown option $arg");
+ }
+ } else {
+ if (entryPoint != null) {
+ print("Multiple entrypoints is not supported.");
+ }
+ entryPoint = Uri.parse(arg);
+ }
+ }
+ if (entryPoint == null) {
+ entryPoint = Uri.parse('dart:core');
+ }
+ Compiler compiler = compilerFor({}, options: ['--analyze-all']);
+ asyncTest(() async {
+ await compiler.runCompiler(entryPoint);
+ testSerialization(compiler.libraryLoader.libraries,
+ outPath: outPath,
+ prettyPrint: prettyPrint);
+ });
+}
+
+void testSerialization(Iterable<LibraryElement> libraries1,
+ {String outPath,
+ bool prettyPrint}) {
+ Serializer serializer = new Serializer(const JsonSerializationEncoder());
+ for (LibraryElement library1 in libraries1) {
+ serializer.serialize(library1);
+ }
+ String text = serializer.toText();
+ String outText = text;
+ if (prettyPrint) {
+ outText = serializer.prettyPrint();
+ }
+ if (outPath != null) {
+ new File(outPath).writeAsStringSync(outText);
+ } else if (prettyPrint) {
+ print(outText);
+ }
+
+ Deserializer deserializer = new Deserializer.fromText(
+ text, const JsonSerializationDecoder());
+ List<LibraryElement> libraries2 = <LibraryElement>[];
+ for (LibraryElement library1 in libraries1) {
+ LibraryElement library2 =
+ deserializer.lookupLibrary(library1.canonicalUri);
+ if (library2 == null) {
+ throw new ArgumentError('No library ${library1.canonicalUri} found.');
+ }
+ checkLibraryContent('library1', 'library2', 'library', library1, library2);
+ libraries2.add(library2);
+ }
+
+ Serializer serializer2 = new Serializer(const JsonSerializationEncoder());
+ for (LibraryElement library2 in libraries2) {
+ serializer2.serialize(library2);
+ }
+ String text2 = serializer2.toText();
+
+ Deserializer deserializer3 = new Deserializer.fromText(
+ text2, const JsonSerializationDecoder());
+ for (LibraryElement library1 in libraries1) {
+ LibraryElement library2 =
+ deserializer.lookupLibrary(library1.canonicalUri);
+ if (library2 == null) {
+ throw new ArgumentError('No library ${library1.canonicalUri} found.');
+ }
+ LibraryElement library3 =
+ deserializer3.lookupLibrary(library1.canonicalUri);
+ if (library3 == null) {
+ throw new ArgumentError('No library ${library1.canonicalUri} found.');
+ }
+ checkLibraryContent('library1', 'library3', 'library', library1, library3);
+ checkLibraryContent('library2', 'library3', 'library', library2, library3);
+ }
+}
+
+/// Check the equivalence of [library1] and [library2] and their content.
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+checkLibraryContent(
+ Object object1, object2, String property,
+ LibraryElement library1, LibraryElement library2) {
+ checkElementProperties(object1, object2, property, library1, library2);
+}
+
+/// Check the equivalence of [element1] and [element2] and their properties.
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+checkElementProperties(
+ Object object1, object2, String property,
+ Element element1, Element element2) {
+ const ElementPropertyEquivalence().visit(element1, element2);
+}
+
+/// Check the equivalence of the two lists of elements, [list1] and [list2].
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+checkElementLists(Object object1, Object object2, String property,
+ Iterable<Element> list1, Iterable<Element> list2) {
+ checkListEquivalence(object1, object2, property,
+ list1, list2, checkElementProperties);
+}
+
+/// Check equivalence of the two lists, [list1] and [list2], using
+/// [checkEquivalence] to check the pair-wise equivalence.
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+void checkListEquivalence(
+ Object object1, Object object2, String property,
+ Iterable list1, Iterable list2,
+ void checkEquivalence(o1, o2, property, a, b)) {
+ for (int i = 0; i < list1.length && i < list2.length; i++) {
+ checkEquivalence(
+ object1, object2, property,
+ list1.elementAt(i), list2.elementAt(i));
+ }
+ for (int i = list1.length; i < list2.length; i++) {
+ throw
+ 'Missing equivalent for element '
+ '#$i ${list2.elementAt(i)} in `${property}` on $object2.\n'
+ '`${property}` on $object1:\n ${list1.join('\n ')}\n'
+ '`${property}` on $object2:\n ${list2.join('\n ')}';
+ }
+ for (int i = list2.length; i < list1.length; i++) {
+ throw
+ 'Missing equivalent for element '
+ '#$i ${list1.elementAt(i)} in `${property}` on $object1.\n'
+ '`${property}` on $object1:\n ${list1.join('\n ')}\n'
+ '`${property}` on $object2:\n ${list2.join('\n ')}';
+ }
+}
+
+/// Checks the equivalence of the identity (but not properties) of [element1]
+/// and [element2].
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+void checkElementIdentities(
+ Object object1, Object object2, String property,
+ Element element1, Element element2) {
+ if (identical(element1, element2)) return;
+ if (element1 == null || element2 == null) {
+ check(object1, object2, property, element1, element2);
+ }
+ const ElementIdentityEquivalence().visit(element1, element2);
+}
+
+/// Checks the pair-wise equivalence of the identity (but not properties) of the
+/// elements in [list] and [list2].
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+void checkElementListIdentities(
+ Object object1, Object object2, String property,
+ Iterable<Element> list1, Iterable<Element> list2) {
+ checkListEquivalence(
+ object1, object2, property,
+ list1, list2, checkElementIdentities);
+}
+
+/// Checks the equivalence of [type1] and [type2].
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+void checkTypes(
+ Object object1, Object object2, String property,
+ DartType type1, DartType type2) {
+ if (identical(type1, type2)) return;
+ if (type1 == null || type2 == null) {
+ check(object1, object2, property, type1, type2);
+ }
+ const TypeEquivalence().visit(type1, type2);
+}
+
+/// Checks the pair-wise equivalence of the types in [list1] and [list2].
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+void checkTypeLists(
+ Object object1, Object object2, String property,
+ List<DartType> list1, List<DartType> list2) {
+ checkListEquivalence(object1, object2, property, list1, list2, checkTypes);
+}
+
+/// Checks the equivalence of [exp1] and [exp2].
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+void checkConstants(
+ Object object1, Object object2, String property,
+ ConstantExpression exp1, ConstantExpression exp2) {
+ if (identical(exp1, exp2)) return;
+ if (exp1 == null || exp2 == null) {
+ check(object1, object2, property, exp1, exp2);
+ }
+ const ConstantEquivalence().visit(exp1, exp2);
+}
+
+/// Checks the pair-wise equivalence of the contants in [list1] and [list2].
+///
+/// Uses [object1], [object2] and [property] to provide context for failures.
+void checkConstantLists(
+ Object object1, Object object2, String property,
+ List<ConstantExpression> list1,
+ List<ConstantExpression> list2) {
+ checkListEquivalence(
+ object1, object2, property,
+ list1, list2, checkConstants);
+}
+
+/// Checks the equivalence of [constructor1] and [constructor2].
+void constantConstructorEquivalence(ConstantConstructor constructor1,
+ ConstantConstructor constructor2) {
+ const ConstantConstructorEquivalence().visit(constructor1, constructor2);
+}
+
+/// Visitor that checks the equivalence of [ConstantConstructor]s.
+class ConstantConstructorEquivalence
+ extends ConstantConstructorVisitor<dynamic, ConstantConstructor> {
+ const ConstantConstructorEquivalence();
+
+ @override
+ void visit(ConstantConstructor constructor1,
+ ConstantConstructor constructor2) {
+ if (identical(constructor1, constructor2)) return;
+ check(constructor1, constructor2, 'kind',
+ constructor1.kind, constructor2.kind);
+ constructor1.accept(this, constructor2);
+ }
+
+ @override
+ visitGenerative(
+ GenerativeConstantConstructor constructor1,
+ GenerativeConstantConstructor constructor2) {
+ checkTypes(
+ constructor1, constructor2, 'type',
+ constructor1.type, constructor2.type);
+ check(constructor1, constructor2, 'defaultValues.length',
+ constructor1.defaultValues.length,
+ constructor2.defaultValues.length);
+ constructor1.defaultValues.forEach((k, v) {
+ checkConstants(
+ constructor1, constructor2, 'defaultValue[$k]',
+ v, constructor2.defaultValues[k]);
+ });
+ check(constructor1, constructor2, 'fieldMap.length',
+ constructor1.fieldMap.length,
+ constructor2.fieldMap.length);
+ constructor1.fieldMap.forEach((k1, v1) {
+ bool matched = false;
+ constructor2.fieldMap.forEach((k2, v2) {
+ if (k1.name == k2.name &&
+ k1.library.canonicalUri == k2.library.canonicalUri) {
+ checkElementIdentities(
+ constructor1, constructor2, 'fieldMap[${k1.name}].key', k1, k2);
+ checkConstants(
+ constructor1, constructor2, 'fieldMap[${k1.name}].value', v1, v2);
+ matched = true;
+ }
+ });
+ if (!matched) {
+ throw 'Unmatched field $k1 = $v1';
+ }
+ });
+ checkConstants(
+ constructor1, constructor2, 'superConstructorInvocation',
+ constructor1.superConstructorInvocation,
+ constructor2.superConstructorInvocation);
+ }
+
+ @override
+ visitRedirectingFactory(
+ RedirectingFactoryConstantConstructor constructor1,
+ RedirectingFactoryConstantConstructor constructor2) {
+ checkConstants(
+ constructor1, constructor2, 'targetConstructorInvocation',
+ constructor1.targetConstructorInvocation,
+ constructor2.targetConstructorInvocation);
+ }
+
+ @override
+ visitRedirectingGenerative(
+ RedirectingGenerativeConstantConstructor constructor1,
+ RedirectingGenerativeConstantConstructor constructor2) {
+ check(constructor1, constructor2, 'defaultValues.length',
+ constructor1.defaultValues.length,
+ constructor2.defaultValues.length);
+ constructor1.defaultValues.forEach((k, v) {
+ checkConstants(
+ constructor1, constructor2, 'defaultValue[$k]',
+ v, constructor2.defaultValues[k]);
+ });
+ checkConstants(
+ constructor1, constructor2, 'thisConstructorInvocation',
+ constructor1.thisConstructorInvocation,
+ constructor2.thisConstructorInvocation);
+ }
+}
+
+/// Check that the values [property] of [object1] and [object2], [value1] and
+/// [value2] respectively, are equal and throw otherwise.
+void check(var object1, var object2, String property, var value1, value2) {
+ if (value1 != value2) {
+ throw "$object1.$property = '${value1}' <> "
+ "$object2.$property = '${value2}'";
+ }
+}
+
+/// Visitor that checks for equivalence of [Element] identities.
+class ElementIdentityEquivalence extends BaseElementVisitor<dynamic, Element> {
+ const ElementIdentityEquivalence();
+
+ void visit(Element element1, Element element2) {
+ check(element1, element2, 'kind', element1.kind, element2.kind);
+ element1.accept(this, element2);
+ }
+
+ @override
+ void visitElement(Element e, Element arg) {
+ throw new UnsupportedError("Unsupported element $e");
+ }
+
+ @override
+ void visitLibraryElement(LibraryElement element1, LibraryElement element2) {
+ check(element1, element2,
+ 'canonicalUri',
+ element1.canonicalUri, element2.canonicalUri);
+ }
+
+ @override
+ void visitCompilationUnitElement(CompilationUnitElement element1,
+ CompilationUnitElement element2) {
+ check(element1, element2,
+ 'name',
+ element1.name, element2.name);
+ visit(element1.library, element2.library);
+ }
+
+ @override
+ void visitClassElement(ClassElement element1, ClassElement element2) {
+ check(element1, element2,
+ 'name',
+ element1.name, element2.name);
+ visit(element1.library, element2.library);
+ }
+
+ void checkMembers(Element element1, Element element2) {
+ check(element1, element2,
+ 'name',
+ element1.name, element2.name);
+ if (element1.enclosingClass != null || element2.enclosingClass != null) {
+ visit(element1.enclosingClass, element2.enclosingClass);
+ } else {
+ visit(element1.library, element2.library);
+ }
+ }
+
+ @override
+ void visitFieldElement(FieldElement element1, FieldElement element2) {
+ checkMembers(element1, element2);
+ }
+
+ @override
+ void visitFunctionElement(FunctionElement element1,
+ FunctionElement element2) {
+ checkMembers(element1, element2);
+ }
+
+ void visitAbstractFieldElement(AbstractFieldElement element1,
+ AbstractFieldElement element2) {
+ checkMembers(element1, element2);
+ }
+
+ @override
+ void visitTypeVariableElement(TypeVariableElement element1,
+ TypeVariableElement element2) {
+ check(element1, element2,
+ 'name',
+ element1.name, element2.name);
+ visit(element1.typeDeclaration, element2.typeDeclaration);
+ }
+
+ @override
+ void visitTypedefElement(TypedefElement element1, TypedefElement element2) {
+ check(element1, element2,
+ 'name',
+ element1.name, element2.name);
+ visit(element1.library, element2.library);
+ }
+
+ @override
+ void visitParameterElement(ParameterElement element1,
+ ParameterElement element2) {
+ check(element1, element2,
+ 'name',
+ element1.name, element2.name);
+ visit(element1.functionDeclaration, element2.functionDeclaration);
+ }
+}
+
+/// Visitor that checks for equivalence of [Element] properties.
+class ElementPropertyEquivalence extends BaseElementVisitor<dynamic, Element> {
+ const ElementPropertyEquivalence();
+
+ void visit(Element element1, Element element2) {
+ if (element1 == element2) return;
+ check(element1, element2, 'kind', element1.kind, element2.kind);
+ element1.accept(this, element2);
+ }
+
+ @override
+ void visitElement(Element e, Element arg) {
+ throw new UnsupportedError("Unsupported element $e");
+ }
+
+ @override
+ void visitLibraryElement(LibraryElement element1, LibraryElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ check(element1, element2, 'name', element1.name, element2.name);
+ check(element1, element2, 'getLibraryName',
+ element1.getLibraryName(), element2.getLibraryName());
+ visitMembers(element1, element2);
+ visit(element1.entryCompilationUnit, element2.entryCompilationUnit);
+ checkElementLists(
+ element1, element2, 'compilationUnits',
+ element1.compilationUnits.toList(),
+ element2.compilationUnits.toList());
+
+ bool filterTags(LibraryTag tag) => tag.asLibraryDependency() != null;
+
+ List<LibraryTag> tags1 = element1.tags.where(filterTags).toList();
+ List<LibraryTag> tags2 = element2.tags.where(filterTags).toList();
+ checkListEquivalence(element1, element2, 'tags', tags1, tags2,
+ (Object object1, Object object2, String property,
+ LibraryDependency tag1, LibraryDependency tag2) {
+ checkElementIdentities(
+ tag1, tag2, 'getLibraryFromTag',
+ element1.getLibraryFromTag(tag1),
+ element2.getLibraryFromTag(tag2));
+ });
+
+ List<Element> imports1 = <Element>[];
+ List<Element> imports2 = <Element>[];
+ element1.forEachImport((Element import) {
+ if (import.isAmbiguous) return;
+ imports1.add(import);
+ });
+ element2.forEachImport((Element import) {
+ if (import.isAmbiguous) return;
+ imports2.add(import);
+ });
+ checkElementListIdentities(
+ element1, element2, 'imports', imports1, imports2);
+
+ List<Element> exports1 = <Element>[];
+ List<Element> exports2 = <Element>[];
+ element1.forEachExport((Element export) {
+ if (export.isAmbiguous) return;
+ exports1.add(export);
+ });
+ element2.forEachExport((Element export) {
+ if (export.isAmbiguous) return;
+ exports2.add(export);
+ });
+ checkElementListIdentities(
+ element1, element2, 'exports', exports1, exports2);
+ }
+
+ @override
+ void visitCompilationUnitElement(CompilationUnitElement element1,
+ CompilationUnitElement element2) {
+ check(element1, element2,
+ 'name',
+ element1.name, element2.name);
+ checkElementIdentities(
+ element1, element2, 'library',
+ element1.library, element2.library);
+ check(element1, element2,
+ 'script.resourceUri',
+ element1.script.resourceUri, element2.script.resourceUri);
+ List<Element> members1 = <Element>[];
+ List<Element> members2 = <Element>[];
+ element1.forEachLocalMember((Element member) {
+ members1.add(member);
+ });
+ element2.forEachLocalMember((Element member) {
+ members2.add(member);
+ });
+ checkElementListIdentities(
+ element1, element2, 'localMembers', members1, members2);
+ }
+
+ void visitMembers(ScopeContainerElement element1,
+ ScopeContainerElement element2) {
+ Set<String> names = new Set<String>();
+ element1.forEachLocalMember((Element member) {
+ names.add(member.name);
+ });
+ element2.forEachLocalMember((Element member) {
+ names.add(member.name);
+ });
+ for (String name in names) {
+ Element member1 = element1.localLookup(name);
+ Element member2 = element2.localLookup(name);
+ if (member1 == null) {
+ print('Missing member for $member2');
+ continue;
+ }
+ if (member2 == null) {
+ print('Missing member for $member1');
+ continue;
+ }
+ visit(member1, member2);
+ }
+ }
+
+ @override
+ void visitClassElement(ClassElement element1, ClassElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ check(element1, element2, 'name',
+ element1.name, element2.name);
+ check(element1, element2, 'sourcePosition',
+ element1.sourcePosition, element2.sourcePosition);
+ checkElementIdentities(
+ element1, element2, 'library',
+ element1.library, element2.library);
+ checkElementIdentities(
+ element1, element2, 'compilationUnit',
+ element1.compilationUnit, element2.compilationUnit);
+ check(element1, element2, 'isObject',
+ element1.isObject, element2.isObject);
+ checkTypeLists(element1, element2, 'typeVariables',
+ element1.typeVariables, element2.typeVariables);
+ check(element1, element2, 'isAbstract',
+ element1.isAbstract, element2.isAbstract);
+ if (!element1.isObject) {
+ checkTypes(element1, element2, 'supertype',
+ element1.supertype, element2.supertype);
+ }
+ check(element1, element2, 'hierarchyDepth',
+ element1.hierarchyDepth, element2.hierarchyDepth);
+ checkTypeLists(
+ element1, element2, 'allSupertypes',
+ element1.allSupertypes.toList(),
+ element2.allSupertypes.toList());
+ OrderedTypeSet typeSet1 = element1.allSupertypesAndSelf;
+ OrderedTypeSet typeSet2 = element1.allSupertypesAndSelf;
+ checkListEquivalence(
+ element1, element2, 'allSupertypes',
+ typeSet1.levelOffsets,
+ typeSet2.levelOffsets,
+ check);
+ check(element1, element2, 'allSupertypesAndSelf.levels',
+ typeSet1.levels, typeSet2.levels);
+ checkTypeLists(
+ element1, element2, 'supertypes',
+ typeSet1.supertypes.toList(),
+ typeSet2.supertypes.toList());
+ checkTypeLists(
+ element1, element2, 'types',
+ typeSet1.types.toList(),
+ typeSet2.types.toList());
+
+ checkTypeLists(
+ element1, element2, 'interfaces',
+ element1.interfaces.toList(),
+ element2.interfaces.toList());
+
+ visitMembers(element1, element2);
+ }
+
+ @override
+ void visitFieldElement(FieldElement element1, FieldElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ check(element1, element2, 'name',
+ element1.name, element2.name);
+ check(element1, element2, 'sourcePosition',
+ element1.sourcePosition, element2.sourcePosition);
+ checkTypes(
+ element1, element2, 'type',
+ element1.type, element2.type);
+ check(element1, element2, 'isConst',
+ element1.isConst, element2.isConst);
+ check(element1, element2, 'isFinal',
+ element1.isFinal, element2.isFinal);
+ if (element1.isConst) {
+ checkConstants(
+ element1, element2, 'constant',
+ element1.constant, element2.constant);
+ }
+ check(element1, element2, 'isTopLevel',
+ element1.isTopLevel, element2.isTopLevel);
+ check(element1, element2, 'isStatic',
+ element1.isStatic, element2.isStatic);
+ check(element1, element2, 'isInstanceMember',
+ element1.isInstanceMember, element2.isInstanceMember);
+
+ checkElementIdentities(
+ element1, element2, 'library',
+ element1.library, element2.library);
+ checkElementIdentities(
+ element1, element2, 'compilationUnit',
+ element1.compilationUnit, element2.compilationUnit);
+ checkElementIdentities(
+ element1, element2, 'enclosingClass',
+ element1.enclosingClass, element2.enclosingClass);
+ }
+
+ @override
+ void visitFunctionElement(FunctionElement element1,
+ FunctionElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ check(element1, element2, 'name',
+ element1.name, element2.name);
+ check(element1, element2, 'sourcePosition',
+ element1.sourcePosition, element2.sourcePosition);
+ checkTypes(
+ element1, element2, 'type',
+ element1.type, element2.type);
+ checkListEquivalence(
+ element1, element2, 'parameters',
+ element1.parameters, element2.parameters,
+ checkElementProperties);
+ check(element1, element2, 'isOperator',
+ element1.isOperator, element2.isOperator);
+
+ checkElementIdentities(
+ element1, element2, 'library',
+ element1.library, element2.library);
+ checkElementIdentities(
+ element1, element2, 'compilationUnit',
+ element1.compilationUnit, element2.compilationUnit);
+ checkElementIdentities(
+ element1, element2, 'enclosingClass',
+ element1.enclosingClass, element2.enclosingClass);
+ }
+
+ @override
+ void visitConstructorElement(ConstructorElement element1,
+ ConstructorElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ checkElementIdentities(
+ element1, element2, 'enclosingClass',
+ element1.enclosingClass, element2.enclosingClass);
+ check(
+ element1, element2, 'name',
+ element1.name, element2.name);
+ check(element1, element2, 'sourcePosition',
+ element1.sourcePosition, element2.sourcePosition);
+ checkListEquivalence(
+ element1, element2, 'parameters',
+ element1.parameters, element2.parameters,
+ checkElementProperties);
+ checkTypes(
+ element1, element2, 'type',
+ element1.type, element2.type);
+ check(element1, element2, 'isConst',
+ element1.isConst, element2.isConst);
+ check(element1, element2, 'isExternal',
+ element1.isExternal, element2.isExternal);
+ if (element1.isConst && !element1.isExternal) {
+ constantConstructorEquivalence(
+ element1.constantConstructor,
+ element2.constantConstructor);
+ }
+ }
+
+ @override
+ void visitAbstractFieldElement(AbstractFieldElement element1,
+ AbstractFieldElement element2) {
+ visit(element1.getter, element2.getter);
+ visit(element1.setter, element2.setter);
+ }
+
+ @override
+ void visitTypeVariableElement(TypeVariableElement element1,
+ TypeVariableElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ check(element1, element2, 'name', element1.name, element2.name);
+ check(element1, element2, 'sourcePosition',
+ element1.sourcePosition, element2.sourcePosition);
+ check(element1, element2, 'index', element1.index, element2.index);
+ checkTypes(
+ element1, element2, 'type',
+ element1.type, element2.type);
+ checkTypes(
+ element1, element2, 'bound',
+ element1.bound, element2.bound);
+ }
+
+ @override
+ void visitTypedefElement(TypedefElement element1,
+ TypedefElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ check(element1, element2, 'name', element1.name, element2.name);
+ check(element1, element2, 'sourcePosition',
+ element1.sourcePosition, element2.sourcePosition);
+ checkTypes(
+ element1, element2, 'alias',
+ element1.alias, element2.alias);
+ checkTypeLists(
+ element1, element2, 'typeVariables',
+ element1.typeVariables, element2.typeVariables);
+ checkElementIdentities(
+ element1, element2, 'library',
+ element1.library, element2.library);
+ checkElementIdentities(
+ element1, element2, 'compilationUnit',
+ element1.compilationUnit, element2.compilationUnit);
+ // TODO(johnniwinther): Check the equivalence of typedef parameters.
+ }
+
+ @override
+ void visitParameterElement(ParameterElement element1,
+ ParameterElement element2) {
+ checkElementIdentities(null, null, null, element1, element2);
+ checkElementIdentities(
+ element1, element2, 'functionDeclaration',
+ element1.functionDeclaration, element2.functionDeclaration);
+ check(element1, element2, 'name', element1.name, element2.name);
+ check(element1, element2, 'sourcePosition',
+ element1.sourcePosition, element2.sourcePosition);
+ checkTypes(
+ element1, element2, 'type',
+ element1.type, element2.type);
+ check(
+ element1, element2, 'isOptional',
+ element1.isOptional, element2.isOptional);
+ check(
+ element1, element2, 'isNamed',
+ element1.isNamed, element2.isNamed);
+ check(element1, element2, 'name', element1.name, element2.name);
+ if (element1.isOptional) {
+ checkConstants(
+ element1, element2, 'constant',
+ element1.constant, element2.constant);
+ }
+ checkElementIdentities(
+ element1, element2, 'compilationUnit',
+ element1.compilationUnit, element2.compilationUnit);
+ }
+
+ @override
+ void visitFieldParameterElement(InitializingFormalElement element1,
+ InitializingFormalElement element2) {
+ visitParameterElement(element1, element2);
+ checkElementIdentities(
+ element1, element2, 'fieldElement',
+ element1.fieldElement, element2.fieldElement);
+ }
+}
+
+/// Visitor that checks for equivalence of [DartType]s.
+class TypeEquivalence implements DartTypeVisitor<dynamic, DartType> {
+ const TypeEquivalence();
+
+ void visit(DartType type1, DartType type2) {
+ check(type1, type2, 'kind', type1.kind, type2.kind);
+ type1.accept(this, type2);
+ }
+
+ @override
+ void visitDynamicType(DynamicType type, DynamicType other) {
+ }
+
+ @override
+ void visitFunctionType(FunctionType type, FunctionType other) {
+ checkTypeLists(
+ type, other, 'parameterTypes',
+ type.parameterTypes, other.parameterTypes);
+ checkTypeLists(
+ type, other, 'optionalParameterTypes',
+ type.optionalParameterTypes, other.optionalParameterTypes);
+ checkTypeLists(
+ type, other, 'namedParameterTypes',
+ type.namedParameterTypes, other.namedParameterTypes);
+ for (int i = 0; i < type.namedParameters.length; i++) {
+ if (type.namedParameters[i] != other.namedParameters[i]) {
+ throw "Named parameter '$type.namedParameters[i]' <> "
+ "'${other.namedParameters[i]}'";
+ }
+ }
+ }
+
+ void visitGenericType(GenericType type, GenericType other) {
+ checkElementIdentities(
+ type, other, 'element',
+ type.element, other.element);
+ checkTypeLists(
+ type, other, 'typeArguments',
+ type.typeArguments, other.typeArguments);
+ }
+
+ @override
+ void visitMalformedType(MalformedType type, MalformedType other) {
+ }
+
+ @override
+ void visitStatementType(StatementType type, StatementType other) {
+ throw new UnsupportedError("Unsupported type: $type");
+ }
+
+ @override
+ void visitTypeVariableType(TypeVariableType type, TypeVariableType other) {
+ checkElementIdentities(
+ type, other, 'element',
+ type.element, other.element);
+ }
+
+ @override
+ void visitVoidType(VoidType type, VoidType argument) {
+ }
+
+ @override
+ void visitInterfaceType(InterfaceType type, InterfaceType other) {
+ visitGenericType(type, other);
+ }
+
+ @override
+ void visitTypedefType(TypedefType type, TypedefType other) {
+ visitGenericType(type, other);
+ }
+}
+
+/// Visitor that checks for structural equivalence of [ConstantExpression]s.
+class ConstantEquivalence
+ implements ConstantExpressionVisitor<dynamic, ConstantExpression> {
+ const ConstantEquivalence();
+
+ @override
+ visit(ConstantExpression exp1, ConstantExpression exp2) {
+ if (identical(exp1, exp2)) return;
+ check(exp1, exp2, 'kind', exp1.kind, exp2.kind);
+ exp1.accept(this, exp2);
+ }
+
+ @override
+ visitBinary(BinaryConstantExpression exp1, BinaryConstantExpression exp2) {
+ check(exp1, exp2, 'operator', exp1.operator, exp2.operator);
+ checkConstants(exp1, exp2, 'left', exp1.left, exp2.left);
+ checkConstants(exp1, exp2, 'right', exp1.right, exp2.right);
+ }
+
+ @override
+ visitConcatenate(ConcatenateConstantExpression exp1,
+ ConcatenateConstantExpression exp2) {
+ checkConstantLists(
+ exp1, exp2, 'expressions',
+ exp1.expressions, exp2.expressions);
+ }
+
+ @override
+ visitConditional(ConditionalConstantExpression exp1,
+ ConditionalConstantExpression exp2) {
+ checkConstants(
+ exp1, exp2, 'condition', exp1.condition, exp2.condition);
+ checkConstants(exp1, exp2, 'trueExp', exp1.trueExp, exp2.trueExp);
+ checkConstants(exp1, exp2, 'falseExp', exp1.falseExp, exp2.falseExp);
+ }
+
+ @override
+ visitConstructed(ConstructedConstantExpression exp1,
+ ConstructedConstantExpression exp2) {
+ checkTypes(
+ exp1, exp2, 'type',
+ exp1.type, exp2.type);
+ checkElementIdentities(
+ exp1, exp2, 'target',
+ exp1.target, exp2.target);
+ checkConstantLists(
+ exp1, exp2, 'arguments',
+ exp1.arguments, exp2.arguments);
+ check(exp1, exp2, 'callStructure', exp1.callStructure, exp2.callStructure);
+ }
+
+ @override
+ visitFunction(FunctionConstantExpression exp1,
+ FunctionConstantExpression exp2) {
+ checkElementIdentities(
+ exp1, exp2, 'element',
+ exp1.element, exp2.element);
+ }
+
+ @override
+ visitIdentical(IdenticalConstantExpression exp1,
+ IdenticalConstantExpression exp2) {
+ checkConstants(exp1, exp2, 'left', exp1.left, exp2.left);
+ checkConstants(exp1, exp2, 'right', exp1.right, exp2.right);
+ }
+
+ @override
+ visitList(ListConstantExpression exp1, ListConstantExpression exp2) {
+ checkTypes(
+ exp1, exp2, 'type',
+ exp1.type, exp2.type);
+ checkConstantLists(
+ exp1, exp2, 'values',
+ exp1.values, exp2.values);
+ }
+
+ @override
+ visitMap(MapConstantExpression exp1, MapConstantExpression exp2) {
+ checkTypes(
+ exp1, exp2, 'type',
+ exp1.type, exp2.type);
+ checkConstantLists(
+ exp1, exp2, 'keys',
+ exp1.keys, exp2.keys);
+ checkConstantLists(
+ exp1, exp2, 'values',
+ exp1.values, exp2.values);
+ }
+
+ @override
+ visitNamed(NamedArgumentReference exp1, NamedArgumentReference exp2) {
+ check(exp1, exp2, 'name', exp1.name, exp2.name);
+ }
+
+ @override
+ visitPositional(PositionalArgumentReference exp1,
+ PositionalArgumentReference exp2) {
+ check(exp1, exp2, 'index', exp1.index, exp2.index);
+ }
+
+ @override
+ visitSymbol(SymbolConstantExpression exp1, SymbolConstantExpression exp2) {
+ // TODO: implement visitSymbol
+ }
+
+ @override
+ visitType(TypeConstantExpression exp1, TypeConstantExpression exp2) {
+ checkTypes(
+ exp1, exp2, 'type',
+ exp1.type, exp2.type);
+ }
+
+ @override
+ visitUnary(UnaryConstantExpression exp1, UnaryConstantExpression exp2) {
+ check(exp1, exp2, 'operator', exp1.operator, exp2.operator);
+ checkConstants(
+ exp1, exp2, 'expression', exp1.expression, exp2.expression);
+ }
+
+ @override
+ visitVariable(VariableConstantExpression exp1,
+ VariableConstantExpression exp2) {
+ checkElementIdentities(
+ exp1, exp2, 'element',
+ exp1.element, exp2.element);
+ }
+
+ @override
+ visitBool(BoolConstantExpression exp1, BoolConstantExpression exp2) {
+ check(exp1, exp2, 'primitiveValue',
+ exp1.primitiveValue, exp2.primitiveValue);
+ }
+
+ @override
+ visitDouble(DoubleConstantExpression exp1, DoubleConstantExpression exp2) {
+ check(exp1, exp2, 'primitiveValue',
+ exp1.primitiveValue, exp2.primitiveValue);
+ }
+
+ @override
+ visitInt(IntConstantExpression exp1, IntConstantExpression exp2) {
+ check(exp1, exp2, 'primitiveValue',
+ exp1.primitiveValue, exp2.primitiveValue);
+ }
+
+ @override
+ visitNull(NullConstantExpression exp1, NullConstantExpression exp2) {
+ // Do nothing.
+ }
+
+ @override
+ visitString(StringConstantExpression exp1, StringConstantExpression exp2) {
+ check(exp1, exp2, 'primitiveValue',
+ exp1.primitiveValue, exp2.primitiveValue);
+ }
+
+ @override
+ visitBoolFromEnvironment(BoolFromEnvironmentConstantExpression exp1,
+ BoolFromEnvironmentConstantExpression exp2) {
+ checkConstants(exp1, exp2, 'name', exp1.name, exp2.name);
+ checkConstants(
+ exp1, exp2, 'defaultValue',
+ exp1.defaultValue, exp2.defaultValue);
+ }
+
+ @override
+ visitIntFromEnvironment(IntFromEnvironmentConstantExpression exp1,
+ IntFromEnvironmentConstantExpression exp2) {
+ checkConstants(exp1, exp2, 'name', exp1.name, exp2.name);
+ checkConstants(
+ exp1, exp2, 'defaultValue',
+ exp1.defaultValue, exp2.defaultValue);
+ }
+
+ @override
+ visitStringFromEnvironment(StringFromEnvironmentConstantExpression exp1,
+ StringFromEnvironmentConstantExpression exp2) {
+ checkConstants(exp1, exp2, 'name', exp1.name, exp2.name);
+ checkConstants(
+ exp1, exp2, 'defaultValue',
+ exp1.defaultValue, exp2.defaultValue);
+ }
+
+ @override
+ visitStringLength(StringLengthConstantExpression exp1,
+ StringLengthConstantExpression exp2) {
+ checkConstants(
+ exp1, exp2, 'expression',
+ exp1.expression, exp2.expression);
+ }
+
+ @override
+ visitDeferred(DeferredConstantExpression exp1,
+ DeferredConstantExpression exp2) {
+ // TODO: implement visitDeferred
+ }
+}
diff --git a/tests/compiler/dart2js/sourcemaps/colors.dart b/tests/compiler/dart2js/sourcemaps/colors.dart
new file mode 100644
index 0000000..8241dd1
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/colors.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2015, 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.
+
+/// Utility library for creating web colors.
+
+library colors;
+
+/// A web color.
+abstract class Color {
+ /// The hexadecimal code for the color, without the prefixed '#'.
+ String get toHex;
+}
+
+/// A web color defined as RGB.
+class RGB implements Color {
+ final double r;
+ final double g;
+ final double b;
+
+ /// Creates a color defined by the amount of red [r], green [g], and blue [b]
+ /// all in range 0..1.
+ const RGB(this.r, this.g, this.b);
+
+ String get toHex {
+ StringBuffer sb = new StringBuffer();
+
+ void writeHex(double value) {
+ int i = (value * 255.0).round();
+ if (i < 16) {
+ sb.write('0');
+ }
+ sb.write(i.toRadixString(16));
+ }
+
+ writeHex(r);
+ writeHex(g);
+ writeHex(b);
+
+ return sb.toString();
+ }
+
+ String toString() => 'rgb($r,$g,$b)';
+}
+
+/// A web color defined as HSV.
+class HSV implements Color {
+ final double h;
+ final double s;
+ final double v;
+
+ /// Creates a color defined by the hue [h] in range 0..360 (360 excluded),
+ /// saturation [s] in range 0..1, and value [v] in range 0..1.
+ const HSV(this.h, this.s, this.v);
+
+ String get toHex => toRGB(this).toHex;
+
+ static RGB toRGB(HSV hsv) {
+ double h = hsv.h;
+ double s = hsv.s;
+ double v = hsv.v;
+ if (s == 0.0) {
+ // Grey.
+ return new RGB(v, v, v);
+ }
+ h /= 60.0; // Sector 0 to 5.
+ int i = h.floor();
+ double f = h - i; // Factorial part of [h].
+ double p = v * (1.0 - s);
+ double q = v * (1.0 - s * f);
+ double t = v * (1.0 - s * (1.0 - f ));
+ switch (i) {
+ case 0:
+ return new RGB(v, t, p);
+ case 1:
+ return new RGB(q, v, p);
+ case 2:
+ return new RGB(p, v, t);
+ case 3:
+ return new RGB(p, q, v);
+ case 4:
+ return new RGB(t, p, v);
+ default: // case 5:
+ return new RGB(v, p, q);
+ }
+ }
+
+ String toString() => 'hsv($h,$s,$v)';
+}
diff --git a/tests/compiler/dart2js/sourcemaps/invokes_test_file.dart b/tests/compiler/dart2js/sourcemaps/invokes_test_file.dart
new file mode 100644
index 0000000..9c06550
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/invokes_test_file.dart
@@ -0,0 +1,127 @@
+// Copyright (c) 2015, 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 file for testing source mappings of invocations.
+
+var counter = 0;
+var bucket;
+
+main(args) {
+ counter++;
+ invokes(args);
+ return counter;
+}
+
+invokes(parameter) {
+ counter++;
+ toplevelFunction();
+ toplevelField();
+ toplevelFinalField();
+ toplevelConstField();
+ toplevelGetter();
+ C.staticFunction();
+ C.staticField();
+ C.staticFinalField();
+ C.staticConstField();
+ C.staticGetter();
+
+ var localVariable = () {
+ counter++;
+ };
+ localFunction() {
+ counter++;
+ }
+
+ parameter();
+ localVariable();
+ localFunction();
+
+ parameter.dynamicInvoke();
+ new C(parameter).instanceInvokes();
+}
+
+toplevelFunction() {
+ counter++;
+}
+
+var toplevelField = () {
+ counter++;
+};
+
+final toplevelFinalField = toplevelFunction;
+
+const toplevelConstField = toplevelFunction;
+
+get toplevelGetter => () {
+ counter++;
+};
+
+typedef F();
+
+class B {
+ B(parameter);
+
+ superMethod() {
+ counter++;
+ }
+
+ var superField = () {
+ counter++;
+ };
+
+ get superGetter => () {
+ counter++;
+ };
+
+}
+
+class C<T> extends B {
+ C(parameter) : super(parameter);
+
+ static staticFunction() {
+ counter++;
+ }
+
+ static var staticField = () {
+ counter++;
+ };
+
+ static final staticFinalField = staticFunction;
+
+ static const staticConstField = staticFunction;
+
+ static get staticGetter => () {
+ counter++;
+ };
+
+ instanceMethod() {
+ counter++;
+ }
+
+ var instanceField = () {
+ counter++;
+ };
+
+ get instanceGetter => () {
+ counter++;
+ };
+
+ instanceInvokes() {
+ instanceMethod();
+ this.instanceMethod();
+ instanceField();
+ this.instanceField();
+ instanceGetter();
+ this.instanceGetter();
+
+ super.superMethod();
+ super.superField();
+ super.superGetter();
+
+ C();
+ dynamic();
+ F();
+ T();
+ }
+}
\ No newline at end of file
diff --git a/tests/compiler/dart2js/sourcemaps/source_mapping_test.dart b/tests/compiler/dart2js/sourcemaps/source_mapping_test.dart
new file mode 100644
index 0000000..831299f
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/source_mapping_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2015, 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 'package:async_helper/async_helper.dart';
+import 'package:expect/expect.dart';
+import 'sourcemap_helper.dart';
+import 'sourcemap_html_helper.dart';
+import 'package:compiler/src/filenames.dart';
+
+main(List<String> arguments) {
+ bool showAll = false;
+ Uri outputUri;
+ if (arguments.isNotEmpty) {
+ outputUri = Uri.base.resolve(nativeToUriPath(arguments.last));
+ showAll = arguments.contains('-a');
+ }
+ asyncTest(() async {
+ String filename =
+ 'tests/compiler/dart2js/sourcemaps/invokes_test_file.dart';
+ SourceMapProcessor processor = new SourceMapProcessor(filename);
+ List<SourceMapInfo> infoList = await processor.process(
+ ['--use-new-source-info', '--csp', '--disable-inlining']);
+ List<SourceMapInfo> userInfoList = <SourceMapInfo>[];
+ List<SourceMapInfo> failureList = <SourceMapInfo>[];
+ for (SourceMapInfo info in infoList) {
+ if (info.element.library.isPlatformLibrary) continue;
+ userInfoList.add(info);
+ Iterable<CodePoint> missingCodePoints =
+ info.codePoints.where((c) => c.isMissing);
+ if (!missingCodePoints.isEmpty) {
+ print('Missing code points ${missingCodePoints} for '
+ '${info.element} in $filename');
+ failureList.add(info);
+ }
+ }
+ if (failureList.isNotEmpty) {
+ if (outputUri == null) {
+ Expect.fail(
+ "Missing code points found. "
+ "Run the test with a URI option, `source_mapping_test <uri>`, to "
+ "create a html visualization of the missing code points.");
+ } else {
+ createTraceSourceMapHtml(outputUri, processor,
+ showAll ? userInfoList : failureList);
+ }
+ } else if (outputUri != null) {
+ createTraceSourceMapHtml(outputUri, processor, userInfoList);
+ }
+ });
+}
diff --git a/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart b/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart
new file mode 100644
index 0000000..d7aa763
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart
@@ -0,0 +1,331 @@
+// Copyright (c) 2015, 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.
+
+library sourcemap.helper;
+
+import 'dart:async';
+import 'package:compiler/src/apiimpl.dart' as api;
+import 'package:compiler/src/dart2jslib.dart' show NullSink;
+import "package:compiler/src/elements/elements.dart";
+import 'package:compiler/src/filenames.dart';
+import 'package:compiler/src/io/source_file.dart';
+import 'package:compiler/src/io/source_information.dart';
+import 'package:compiler/src/js/js.dart' as js;
+import 'package:compiler/src/js/js_debug.dart';
+import 'package:compiler/src/js/js_source_mapping.dart';
+import 'package:compiler/src/js_backend/js_backend.dart';
+import 'package:compiler/src/source_file_provider.dart';
+import '../memory_compiler.dart';
+import '../output_collector.dart';
+
+class OutputProvider {
+ BufferedEventSink jsMapOutput;
+
+ EventSink<String> call(String name, String extension) {
+ if (extension == 'js.map') {
+ return jsMapOutput = new BufferedEventSink();
+ }
+ return new NullSink('$name.$extension');
+ }
+}
+
+class CloningOutputProvider extends OutputProvider {
+ RandomAccessFileOutputProvider outputProvider;
+
+ CloningOutputProvider(Uri jsUri, Uri jsMapUri)
+ : outputProvider = new RandomAccessFileOutputProvider(jsUri, jsMapUri);
+
+ EventSink<String> call(String name, String extension) {
+ EventSink<String> output = outputProvider(name, extension);
+ if (extension == 'js.map') {
+ output = new CloningEventSink(
+ [output, jsMapOutput = new BufferedEventSink()]);
+ }
+ return output;
+ }
+}
+
+abstract class SourceFileManager {
+ SourceFile getSourceFile(var uri);
+}
+
+class ProviderSourceFileManager implements SourceFileManager {
+ final SourceFileProvider sourceFileProvider;
+
+ ProviderSourceFileManager(this.sourceFileProvider);
+
+ @override
+ SourceFile getSourceFile(uri) {
+ return sourceFileProvider.getSourceFile(uri);
+ }
+}
+
+class RecordingPrintingContext extends LenientPrintingContext {
+ CodePositionListener listener;
+
+ RecordingPrintingContext(this.listener);
+
+ @override
+ void exitNode(js.Node node,
+ int startPosition,
+ int endPosition,
+ int closingPosition) {
+ listener.onPositions(
+ node, startPosition, endPosition, closingPosition);
+ }
+}
+
+/// Processor that computes [SourceMapInfo] for the JavaScript compiled for a
+/// given Dart file.
+class SourceMapProcessor {
+ /// If `true` the output from the compilation is written to files.
+ final bool outputToFile;
+
+ /// The [Uri] of the Dart entrypoint.
+ Uri inputUri;
+
+ /// The name of the JavaScript output file.
+ String jsPath;
+
+ /// The [Uri] of the JavaScript output file.
+ Uri targetUri;
+
+ /// The [Uri] of the JavaScript source map file.
+ Uri sourceMapFileUri;
+
+ /// The [SourceFileManager] created for the processing.
+ SourceFileManager sourceFileManager;
+
+ /// Creates a processor for the Dart file [filename].
+ SourceMapProcessor(String filename, {this.outputToFile: false}) {
+ inputUri = Uri.base.resolve(nativeToUriPath(filename));
+ jsPath = 'out.js';
+ targetUri = Uri.base.resolve(jsPath);
+ sourceMapFileUri = Uri.base.resolve('${jsPath}.map');
+ }
+
+ /// Computes the [SourceMapInfo] for the compiled elements.
+ Future<List<SourceMapInfo>> process(List<String> options) async {
+ OutputProvider outputProvider = outputToFile
+ ? new OutputProvider()
+ : new CloningOutputProvider(targetUri, sourceMapFileUri);
+ if (options.contains('--use-new-source-info')) {
+ print('Using the new source information system.');
+ useNewSourceInfo = true;
+ }
+ api.Compiler compiler = await compilerFor({},
+ outputProvider: outputProvider,
+ options: ['--out=$targetUri', '--source-map=$sourceMapFileUri']
+ ..addAll(options));
+ if (options.contains('--disable-inlining')) {
+ print('Inlining disabled');
+ compiler.disableInlining = true;
+ }
+
+ JavaScriptBackend backend = compiler.backend;
+ var handler = compiler.handler;
+ SourceFileProvider sourceFileProvider = handler.provider;
+ sourceFileManager = new ProviderSourceFileManager(sourceFileProvider);
+ await compiler.runCompiler(inputUri);
+
+ List<SourceMapInfo> infoList = <SourceMapInfo>[];
+ backend.generatedCode.forEach((Element element, js.Expression node) {
+ js.JavaScriptPrintingOptions options =
+ new js.JavaScriptPrintingOptions();
+ JavaScriptSourceInformationStrategy sourceInformationStrategy =
+ compiler.backend.sourceInformationStrategy;
+ NodeToSourceLocationsMap nodeMap = new NodeToSourceLocationsMap();
+ SourceInformationProcessor sourceInformationProcessor =
+ sourceInformationStrategy.createProcessor(nodeMap);
+ RecordingPrintingContext printingContext =
+ new RecordingPrintingContext(sourceInformationProcessor);
+ new js.Printer(options, printingContext).visit(node);
+ sourceInformationProcessor.process(node);
+
+ String code = printingContext.getText();
+ CodePointComputer visitor =
+ new CodePointComputer(sourceFileManager, code, nodeMap);
+ visitor.apply(node);
+ List<CodePoint> codePoints = visitor.codePoints;
+ infoList.add(new SourceMapInfo(element, code, node, codePoints, nodeMap));
+ });
+
+ return infoList;
+ }
+}
+
+/// Source mapping information for the JavaScript code of an [Element].
+class SourceMapInfo {
+ final String name;
+ final Element element;
+ final String code;
+ final js.Expression node;
+ final List<CodePoint> codePoints;
+ final NodeToSourceLocationsMap nodeMap;
+
+ SourceMapInfo(
+ Element element, this.code, this.node, this.codePoints, this.nodeMap)
+ : this.name = computeElementNameForSourceMaps(element),
+ this.element = element;
+}
+
+/// Collection of JavaScript nodes with their source mapped target offsets
+/// and source locations.
+class NodeToSourceLocationsMap implements SourceMapper {
+ final Map<js.Node, Map<int, List<SourceLocation>>> _nodeMap = {};
+
+ @override
+ void register(js.Node node, int codeOffset, SourceLocation sourceLocation) {
+ _nodeMap.putIfAbsent(node, () => {})
+ .putIfAbsent(codeOffset, () => [])
+ .add(sourceLocation);
+ }
+
+ Iterable<js.Node> get nodes => _nodeMap.keys;
+
+ Map<int, List<SourceLocation>> operator[] (js.Node node) {
+ return _nodeMap[node];
+ }
+}
+
+/// Visitor that computes the [CodePoint]s for source mapping locations.
+class CodePointComputer extends js.BaseVisitor {
+ final SourceFileManager sourceFileManager;
+ final String code;
+ final NodeToSourceLocationsMap nodeMap;
+ List<CodePoint> codePoints = [];
+
+ CodePointComputer(this.sourceFileManager, this.code, this.nodeMap);
+
+ String nodeToString(js.Node node) {
+ js.JavaScriptPrintingOptions options = new js.JavaScriptPrintingOptions(
+ shouldCompressOutput: true,
+ preferSemicolonToNewlineInMinifiedOutput: true);
+ LenientPrintingContext printingContext = new LenientPrintingContext();
+ new js.Printer(options, printingContext).visit(node);
+ return printingContext.buffer.toString();
+ }
+
+ String positionToString(int position) {
+ String line = code.substring(position);
+ int nl = line.indexOf('\n');
+ if (nl != -1) {
+ line = line.substring(0, nl);
+ }
+ return line;
+ }
+
+ void register(String kind, js.Node node, {bool expectInfo: true}) {
+
+ String dartCodeFromSourceLocation(SourceLocation sourceLocation) {
+ SourceFile sourceFile =
+ sourceFileManager.getSourceFile(sourceLocation.sourceUri);
+ return sourceFile.getLineText(sourceLocation.line)
+ .substring(sourceLocation.column).trim();
+ }
+
+ void addLocation(SourceLocation sourceLocation, String jsCode) {
+ if (sourceLocation == null) {
+ if (expectInfo) {
+ SourceInformation sourceInformation = node.sourceInformation;
+ SourceLocation sourceLocation;
+ String dartCode;
+ if (sourceInformation != null) {
+ sourceLocation = sourceInformation.sourceLocations.first;
+ dartCode = dartCodeFromSourceLocation(sourceLocation);
+ }
+ codePoints.add(new CodePoint(
+ kind, jsCode, sourceLocation, dartCode, isMissing: true));
+ }
+ } else {
+ codePoints.add(new CodePoint(kind, jsCode, sourceLocation,
+ dartCodeFromSourceLocation(sourceLocation)));
+ }
+ }
+
+ Map<int, List<SourceLocation>> locationMap = nodeMap[node];
+ if (locationMap == null) {
+ addLocation(null, nodeToString(node));
+ } else {
+ locationMap.forEach((int targetOffset, List<SourceLocation> locations) {
+ String jsCode = positionToString(targetOffset);
+ for (SourceLocation location in locations) {
+ addLocation(location, jsCode);
+ }
+ });
+ }
+ }
+
+ void apply(js.Node node) {
+ node.accept(this);
+ }
+
+ void visitNode(js.Node node) {
+ register('${node.runtimeType}', node, expectInfo: false);
+ super.visitNode(node);
+ }
+
+ @override
+ void visitNew(js.New node) {
+ node.arguments.forEach(apply);
+ register('New', node);
+ }
+
+ @override
+ void visitReturn(js.Return node) {
+ if (node.value != null) {
+ apply(node.value);
+ }
+ register('Return', node);
+ }
+
+ @override
+ void visitCall(js.Call node) {
+ apply(node.target);
+ node.arguments.forEach(apply);
+ register('Call (${node.target.runtimeType})', node);
+ }
+
+ @override
+ void visitFun(js.Fun node) {
+ node.visitChildren(this);
+ register('Fun', node);
+ }
+
+ @override
+ visitExpressionStatement(js.ExpressionStatement node) {
+ node.visitChildren(this);
+ }
+
+ @override
+ visitBinary(js.Binary node) {
+ node.visitChildren(this);
+ }
+
+ @override
+ visitAccess(js.PropertyAccess node) {
+ node.visitChildren(this);
+ }
+}
+
+/// A JavaScript code point and its mapped dart source location.
+class CodePoint {
+ final String kind;
+ final String jsCode;
+ final SourceLocation sourceLocation;
+ final String dartCode;
+ final bool isMissing;
+
+ CodePoint(
+ this.kind,
+ this.jsCode,
+ this.sourceLocation,
+ this.dartCode,
+ {this.isMissing: false});
+
+ String toString() {
+ return 'CodePoint[kind=$kind,js=$jsCode,dart=$dartCode,'
+ 'location=$sourceLocation]';
+ }
+}
diff --git a/tests/compiler/dart2js/sourcemaps/sourcemap_html_helper.dart b/tests/compiler/dart2js/sourcemaps/sourcemap_html_helper.dart
new file mode 100644
index 0000000..eb1399a
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/sourcemap_html_helper.dart
@@ -0,0 +1,430 @@
+// Copyright (c) 2015, 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.
+
+/// Helper for creating HTML visualization of the source map information
+/// generated by a [SourceMapProcessor].
+
+library sourcemap.html.helper;
+
+import 'dart:convert';
+
+import 'package:compiler/src/io/source_file.dart';
+import 'package:compiler/src/io/source_information.dart';
+import 'package:compiler/src/js/js.dart' as js;
+
+import 'colors.dart';
+import 'sourcemap_helper.dart';
+import 'sourcemap_html_templates.dart';
+
+/// Returns the [index]th color for visualization.
+String toColor(int index) {
+ int hueCount = 24;
+ double h = 360.0 * (index % hueCount) / hueCount;
+ double v = 1.0;
+ double s = 0.5;
+ HSV hsv = new HSV(h, s, v);
+ RGB rgb = HSV.toRGB(hsv);
+ return rgb.toHex;
+}
+
+/// Return the html for the [index] line number.
+String lineNumber(int index) {
+ return '<span class="lineNumber">${index + 1} </span>';
+}
+
+/// Return the html escaped [text].
+String escape(String text) {
+ return const HtmlEscape().convert(text);
+}
+
+/// Information needed to generate HTML for a single [SourceMapInfo].
+class SourceMapHtmlInfo {
+ final SourceMapInfo sourceMapInfo;
+ final CodeProcessor codeProcessor;
+ final SourceLocationCollection sourceLocationCollection;
+
+ SourceMapHtmlInfo(this.sourceMapInfo,
+ this.codeProcessor,
+ this.sourceLocationCollection);
+}
+
+/// A collection of source locations.
+///
+/// Used to index source locations for visualization and linking.
+class SourceLocationCollection {
+ List<SourceLocation> sourceLocations = [];
+ Map<SourceLocation, int> sourceLocationIndexMap;
+
+ SourceLocationCollection([SourceLocationCollection parent])
+ : sourceLocationIndexMap =
+ parent == null ? {} : parent.sourceLocationIndexMap;
+
+ int registerSourceLocation(SourceLocation sourceLocation) {
+ return sourceLocationIndexMap.putIfAbsent(sourceLocation, () {
+ sourceLocations.add(sourceLocation);
+ return sourceLocationIndexMap.length;
+ });
+ }
+
+ int getIndex(SourceLocation sourceLocation) {
+ return sourceLocationIndexMap[sourceLocation];
+ }
+}
+
+/// Processor that computes the HTML representation of a block of JavaScript
+/// code and collects the source locations mapped in the code.
+class CodeProcessor {
+ int lineIndex = 0;
+ final String onclick;
+ int currentJsSourceOffset = 0;
+ final SourceLocationCollection collection;
+ final Map<int, List<SourceLocation>> codeLocations = {};
+
+ CodeProcessor(this.onclick, this.collection);
+
+ void addSourceLocation(int targetOffset, SourceLocation sourceLocation) {
+ codeLocations.putIfAbsent(targetOffset, () => []).add(sourceLocation);
+ collection.registerSourceLocation(sourceLocation);
+ }
+
+ String convertToHtml(String text) {
+ StringBuffer htmlBuffer = new StringBuffer();
+ int offset = 0;
+ int lineIndex = 0;
+ bool pendingSourceLocationsEnd = false;
+ htmlBuffer.write(lineNumber(lineIndex));
+ SourceLocation currentLocation;
+
+ void endCurrentLocation() {
+ if (currentLocation != null) {
+ htmlBuffer.write('</a>');
+ }
+ currentLocation = null;
+ }
+
+ void addSubstring(int until) {
+ if (until <= offset) return;
+
+ String substring = text.substring(offset, until);
+ offset = until;
+ bool first = true;
+ for (String line in substring.split('\n')) {
+ if (!first) {
+ endCurrentLocation();
+ htmlBuffer.write('\n');
+ lineIndex++;
+ htmlBuffer.write(lineNumber(lineIndex));
+ }
+ htmlBuffer.write(escape(line));
+ first = false;
+ }
+ }
+
+ void insertSourceLocations(List<SourceLocation> lastSourceLocations) {
+ endCurrentLocation();
+
+ String color;
+ int index;
+ String title;
+ if (lastSourceLocations.length == 1) {
+ SourceLocation sourceLocation = lastSourceLocations.single;
+ if (sourceLocation != null) {
+ index = collection.getIndex(sourceLocation);
+ color = "background:#${toColor(index)};";
+ title = sourceLocation.shortText;
+ currentLocation = sourceLocation;
+ }
+ } else {
+
+ index = collection.getIndex(lastSourceLocations.first);
+ StringBuffer sb = new StringBuffer();
+ double delta = 100.0 / (lastSourceLocations.length);
+ double position = 0.0;
+
+ void addColor(String color) {
+ sb.write(', ${color} ${position.toInt()}%');
+ position += delta;
+ sb.write(', ${color} ${position.toInt()}%');
+ }
+
+ for (SourceLocation sourceLocation in lastSourceLocations) {
+ if (sourceLocation == null) continue;
+ int colorIndex = collection.getIndex(sourceLocation);
+ addColor('#${toColor(colorIndex)}');
+ currentLocation = sourceLocation;
+ }
+ color = 'background: linear-gradient(to right${sb}); '
+ 'background-size: 10px 10px;';
+ title = lastSourceLocations.map((l) => l.shortText).join(',');
+ }
+ if (index != null) {
+ Set<int> indices =
+ lastSourceLocations.map((l) => collection.getIndex(l)).toSet();
+ String onmouseover = indices.map((i) => '\'$i\'').join(',');
+ htmlBuffer.write(
+ '<a name="js$index" href="#${index}" style="$color" title="$title" '
+ 'onclick="${onclick}" onmouseover="highlight([${onmouseover}]);"'
+ 'onmouseout="highlight([]);">');
+ pendingSourceLocationsEnd = true;
+ }
+ if (lastSourceLocations.last == null) {
+ endCurrentLocation();
+ }
+ }
+
+ for (int targetOffset in codeLocations.keys.toList()..sort()) {
+ List<SourceLocation> sourceLocations = codeLocations[targetOffset];
+ addSubstring(targetOffset);
+ insertSourceLocations(sourceLocations);
+ }
+
+ addSubstring(text.length);
+ endCurrentLocation();
+ return htmlBuffer.toString();
+ }
+}
+
+/// Computes the HTML representation for a collection of JavaScript code blocks.
+String computeJsHtml(Iterable<SourceMapHtmlInfo> infoList) {
+
+ StringBuffer jsCodeBuffer = new StringBuffer();
+ for (SourceMapHtmlInfo info in infoList) {
+ String name = info.sourceMapInfo.name;
+ String html = info.codeProcessor.convertToHtml(info.sourceMapInfo.code);
+ String onclick = 'show(\'$name\');';
+ jsCodeBuffer.write(
+ '<h3 onclick="$onclick">JS code for: ${escape(name)}</h3>\n');
+ jsCodeBuffer.write('''
+<pre>
+$html
+</pre>
+''');
+ }
+ return jsCodeBuffer.toString();
+}
+
+/// Computes the HTML representation of the source mapping information for a
+/// collection of JavaScript code blocks.
+String computeJsTraceHtml(Iterable<SourceMapHtmlInfo> infoList) {
+ StringBuffer jsTraceBuffer = new StringBuffer();
+ for (SourceMapHtmlInfo info in infoList) {
+ String name = info.sourceMapInfo.name;
+ String jsTrace = computeJsTraceHtmlPart(
+ info.sourceMapInfo.codePoints, info.sourceLocationCollection);
+ jsTraceBuffer.write('''
+<div name="$name" class="js-trace-buffer" style="display:none;">
+<h3>Trace for: ${escape(name)}</h3>
+$jsTrace
+</div>
+''');
+ }
+ return jsTraceBuffer.toString();
+}
+
+/// Computes the HTML information for the [info].
+SourceMapHtmlInfo createHtmlInfo(SourceLocationCollection collection,
+ SourceMapInfo info) {
+ js.Node node = info.node;
+ String code = info.code;
+ String name = info.name;
+ String onclick = 'show(\'$name\');';
+ SourceLocationCollection subcollection =
+ new SourceLocationCollection(collection);
+ CodeProcessor codeProcessor = new CodeProcessor(onclick, subcollection);
+ for (js.Node node in info.nodeMap.nodes) {
+ info.nodeMap[node].forEach(
+ (int targetOffset, List<SourceLocation> sourceLocations) {
+ for (SourceLocation sourceLocation in sourceLocations) {
+ codeProcessor.addSourceLocation(targetOffset, sourceLocation);
+ }
+ });
+ }
+ return new SourceMapHtmlInfo(info, codeProcessor, subcollection);
+}
+
+/// Outputs a HTML file in [jsMapHtmlUri] containing an interactive
+/// visualization of the source mapping information in [infoList] computed
+/// with the [sourceMapProcessor].
+void createTraceSourceMapHtml(Uri jsMapHtmlUri,
+ SourceMapProcessor sourceMapProcessor,
+ Iterable<SourceMapInfo> infoList) {
+ SourceFileManager sourceFileManager = sourceMapProcessor.sourceFileManager;
+ SourceLocationCollection collection = new SourceLocationCollection();
+ List<SourceMapHtmlInfo> htmlInfoList = <SourceMapHtmlInfo>[];
+ for (SourceMapInfo info in infoList) {
+ htmlInfoList.add(createHtmlInfo(collection, info));
+ }
+
+ String jsCode = computeJsHtml(htmlInfoList);
+ String dartCode = computeDartHtml(sourceFileManager, htmlInfoList);
+
+ String jsTraceHtml = computeJsTraceHtml(htmlInfoList);
+ outputJsDartTrace(jsMapHtmlUri, jsCode, dartCode, jsTraceHtml);
+ print('Trace source map html generated: $jsMapHtmlUri');
+}
+
+/// Computes the HTML representation for the Dart code snippets referenced in
+/// [infoList].
+String computeDartHtml(
+ SourceFileManager sourceFileManager,
+ Iterable<SourceMapHtmlInfo> infoList) {
+
+ StringBuffer dartCodeBuffer = new StringBuffer();
+ for (SourceMapHtmlInfo info in infoList) {
+ dartCodeBuffer.write(computeDartHtmlPart(info.sourceMapInfo.name,
+ sourceFileManager, info.sourceLocationCollection));
+ }
+ return dartCodeBuffer.toString();
+
+}
+
+/// Computes the HTML representation for the Dart code snippets in [collection].
+String computeDartHtmlPart(String name,
+ SourceFileManager sourceFileManager,
+ SourceLocationCollection collection,
+ {bool showAsBlock: false}) {
+ const int windowSize = 3;
+ StringBuffer dartCodeBuffer = new StringBuffer();
+ Map<Uri, Map<int, List<SourceLocation>>> sourceLocationMap = {};
+ collection.sourceLocations.forEach((SourceLocation sourceLocation) {
+ Map<int, List<SourceLocation>> uriMap =
+ sourceLocationMap.putIfAbsent(sourceLocation.sourceUri, () => {});
+ List<SourceLocation> lineList =
+ uriMap.putIfAbsent(sourceLocation.line, () => []);
+ lineList.add(sourceLocation);
+ });
+ sourceLocationMap.forEach((Uri uri, Map<int, List<SourceLocation>> uriMap) {
+ SourceFile sourceFile = sourceFileManager.getSourceFile(uri);
+ StringBuffer codeBuffer = new StringBuffer();
+
+ int firstLineIndex;
+ int lastLineIndex;
+
+ void flush() {
+ if (firstLineIndex != null && lastLineIndex != null) {
+ dartCodeBuffer.write(
+ '<h4>${uri.pathSegments.last}, '
+ '${firstLineIndex - windowSize + 1}-'
+ '${lastLineIndex + windowSize + 1}'
+ '</h4>\n');
+ dartCodeBuffer.write('<pre>\n');
+ for (int line = firstLineIndex - windowSize;
+ line < firstLineIndex;
+ line++) {
+ if (line >= 0) {
+ dartCodeBuffer.write(lineNumber(line));
+ dartCodeBuffer.write(sourceFile.getLineText(line));
+ }
+ }
+ dartCodeBuffer.write(codeBuffer);
+ for (int line = lastLineIndex + 1;
+ line <= lastLineIndex + windowSize;
+ line++) {
+ if (line < sourceFile.lines) {
+ dartCodeBuffer.write(lineNumber(line));
+ dartCodeBuffer.write(sourceFile.getLineText(line));
+ }
+ }
+ dartCodeBuffer.write('</pre>\n');
+ firstLineIndex = null;
+ lastLineIndex = null;
+ }
+ codeBuffer.clear();
+ }
+
+ List<int> lineIndices = uriMap.keys.toList()..sort();
+ lineIndices.forEach((int lineIndex) {
+ List<SourceLocation> locations = uriMap[lineIndex];
+ if (lastLineIndex != null &&
+ lastLineIndex + windowSize * 4 < lineIndex) {
+ flush();
+ }
+ if (firstLineIndex == null) {
+ firstLineIndex = lineIndex;
+ } else {
+ for (int line = lastLineIndex + 1; line < lineIndex; line++) {
+ codeBuffer.write(lineNumber(line));
+ codeBuffer.write(sourceFile.getLineText(line));
+ }
+ }
+ String line = sourceFile.getLineText(lineIndex);
+ locations.sort((a, b) => a.offset.compareTo(b.offset));
+ for (int i = 0; i < locations.length; i++) {
+ SourceLocation sourceLocation = locations[i];
+ int index = collection.getIndex(sourceLocation);
+ int start = sourceLocation.column;
+ int end = line.length;
+ if (i + 1 < locations.length) {
+ end = locations[i + 1].column;
+ }
+ if (i == 0) {
+ codeBuffer.write(lineNumber(lineIndex));
+ codeBuffer.write(line.substring(0, start));
+ }
+ codeBuffer.write(
+ '<a name="${index}" style="background:#${toColor(index)};" '
+ 'title="[${lineIndex + 1},${start + 1}]" '
+ 'onmouseover="highlight(\'$index\');" '
+ 'onmouseout="highlight();">');
+ codeBuffer.write(line.substring(start, end));
+ codeBuffer.write('</a>');
+ }
+ lastLineIndex = lineIndex;
+ });
+
+ flush();
+ });
+ String display = showAsBlock ? 'block' : 'none';
+ return '''
+<div name="$name" class="dart-buffer" style="display:$display;">
+<h3>Dart code for: ${escape(name)}</h3>
+${dartCodeBuffer}
+</div>''';
+}
+
+/// Computes a HTML visualization of the [codePoints].
+String computeJsTraceHtmlPart(List<CodePoint> codePoints,
+ SourceLocationCollection collection) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.write('<table style="width:100%;">');
+ buffer.write(
+ '<tr><th>Node kind</th><th>JS code @ offset</th>'
+ '<th>Dart code @ mapped location</th><th>file:position:name</th></tr>');
+ codePoints.forEach((CodePoint codePoint) {
+ String jsCode = codePoint.jsCode;
+ if (jsCode.length > 40) {
+ jsCode = jsCode.substring(0, 40);
+ }
+ if (codePoint.sourceLocation != null) {
+ int index = collection.getIndex(codePoint.sourceLocation);
+ if (index != null) {
+
+ buffer.write('<tr style="background:#${toColor(index)};" '
+ 'name="trace$index" '
+ 'onmouseover="highlight([${index}]);"'
+ 'onmouseout="highlight([]);">');
+ } else {
+ buffer.write('<tr>');
+ print('${codePoint.sourceLocation} not found in ');
+ collection.sourceLocationIndexMap.keys
+ .where((l) => l.sourceUri == codePoint.sourceLocation.sourceUri)
+ .forEach((l) => print(' $l'));
+ }
+ } else {
+ buffer.write('<tr>');
+ }
+ buffer.write('<td>${codePoint.kind}</td>');
+ buffer.write('<td class="code">${jsCode}</td>');
+ if (codePoint.sourceLocation == null) {
+ //buffer.write('<td></td>');
+ } else {
+ buffer.write('<td class="code">${codePoint.dartCode}</td>');
+ buffer.write('<td>${escape(codePoint.sourceLocation.shortText)}</td>');
+ }
+ buffer.write('</tr>');
+ });
+ buffer.write('</table>');
+
+ return buffer.toString();
+}
diff --git a/tests/compiler/dart2js/sourcemaps/sourcemap_html_templates.dart b/tests/compiler/dart2js/sourcemaps/sourcemap_html_templates.dart
new file mode 100644
index 0000000..573b902
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/sourcemap_html_templates.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2015, 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.
+
+/// Templates used for the HTML visualization of source map information.
+
+library sourcemap.html.templates;
+
+import 'dart:io';
+
+/// Outputs JavaScript/Dart source mapping traces into [uri].
+void outputJsDartTrace(
+ Uri uri,
+ String jsCodeHtml,
+ String dartCodeHtml,
+ String jsTraceHtml) {
+ String html = '''
+<div class="js-buffer">
+${jsCodeHtml}
+</div>
+${dartCodeHtml}
+${jsTraceHtml}
+''';
+ String css = '''
+.js-buffer {
+ left:0%;
+ width:50%;
+ top:0%;
+ height:50%;
+}
+.dart-buffer {
+ right:0%;
+ width:50%;
+ top:0%;
+ height:50%;
+}
+.js-trace-buffer {
+ left:0%;
+ width:100%;
+ top:50%;
+ height:50%;
+}
+''';
+ outputInTemplate(uri, html, css);
+}
+
+/// Outputs [html] with customized [css] in [uri].
+void outputInTemplate(Uri uri,
+ String html,
+ String css) {
+ output(uri, '''
+<html>
+<head>
+<style>
+a, a:hover {
+ text-decoration: none;
+ color: #000;
+}
+h3 {
+ cursor: pointer;
+}
+.lineNumber {
+ font-size: smaller;
+ color: #888;
+}
+.buffer, .js-buffer, .dart-buffer, .js-trace-buffer {
+ position:fixed;
+ top:0px;
+ height:100%;
+ overflow:auto;
+}
+$css,
+.code {
+ font-family: monospace;
+}
+</style>
+</head>
+<body>
+<script>
+function setAll(name, property, value) {
+ var elements = document.getElementsByName(name);
+ for (var i = 0; i < elements.length; i++) {
+ elements[i].style[property] = value;
+ }
+}
+
+var shownName;
+function show(name) {
+ if (shownName != name) {
+ if (shownName) {
+ setAll(shownName, 'display', 'none');
+ }
+ shownName = name;
+ if (shownName) {
+ setAll(shownName, 'display', 'block');
+ }
+ }
+}
+var highlightNames = [];
+function highlight(names) {
+ var property = 'text-decoration';
+ var onValue = 'underline';
+ var offValue = 'none';
+ if (highlightNames != names) {
+ if (highlightNames && highlightNames.length > 0) {
+ for (var index in highlightNames) {
+ var highlightName = highlightNames[index];
+ setAll(highlightName, property, offValue);
+ setAll('js' + highlightName, property, offValue);
+ setAll('trace' + highlightName, property, offValue);
+ }
+ }
+ highlightNames = names;
+ if (highlightNames && highlightNames.length > 0) {
+ for (var index in highlightNames) {
+ var highlightName = highlightNames[index];
+ setAll(highlightName, property, onValue);
+ setAll('js' + highlightName, property, onValue);
+ setAll('trace' + highlightName, property, onValue);
+ }
+ }
+ }
+}
+</script>
+$html
+</body>
+</html>
+''');
+}
+
+/// Outputs [html] in [uri].
+void output(Uri uri,
+ String html) {
+ File outputFile = new File.fromUri(uri);
+ outputFile.writeAsStringSync(html);
+}
diff --git a/tests/compiler/dart2js/token_naming_test.dart b/tests/compiler/dart2js/token_naming_test.dart
new file mode 100644
index 0000000..7f99d5e
--- /dev/null
+++ b/tests/compiler/dart2js/token_naming_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2015, 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 "package:compiler/src/js_backend/js_backend.dart" show TokenScope;
+
+import "package:expect/expect.dart";
+
+String forwardN(TokenScope scope, int N) {
+ for (int i = 1; i < N; ++i) {
+ scope.getNextName();
+ }
+
+ return scope.getNextName();
+}
+
+int main() {
+ // Test a normal scope.
+ TokenScope scope = new TokenScope();
+
+ // We start with 'a'.
+ Expect.equals("a", scope.getNextName());
+ // We have 24 lower case characters, as s and g are illegal.
+ Expect.equals("A", forwardN(scope, 24));
+ // Make it overflow by skipping all uppercase.
+ Expect.equals("a_", forwardN(scope, 26));
+ // Now numbers.
+ Expect.equals("a0", forwardN(scope, 1));
+ Expect.equals("a9", forwardN(scope, 9));
+ // Then lower case letters.
+ Expect.equals("aa", forwardN(scope, 1));
+ Expect.equals("az", forwardN(scope, 25));
+ // Then upper case letters
+ Expect.equals("aA", forwardN(scope, 1));
+ Expect.equals("aZ", forwardN(scope, 25));
+ // Overflow to first position.
+ Expect.equals("b_", forwardN(scope, 1));
+ // Make sure we skipe g. We have 1 + 10 + 26 + 26 = 63 digits.
+ Expect.equals("h_", forwardN(scope, 63 * 5));
+ // Likewise, ensure we skip s.
+ Expect.equals("t_", forwardN(scope, 63 * 11));
+ // And wrap around another digit.
+ Expect.equals("a__", forwardN(scope, 63 * 33));
+
+ // Test a filtered scope.
+ Set<String> illegal = new Set.from(["b", "aa"]);
+ scope = new TokenScope(illegal);
+
+ // We start with 'a'.
+ Expect.equals("a", forwardN(scope, 1));
+ // Make sure 'b' is skipped.
+ Expect.equals("c", forwardN(scope, 1));
+ // We have 24 lower case characters, as s and g are illegal.
+ Expect.equals("A", forwardN(scope, 22));
+ // Make it overflow by skipping all uppercase.
+ Expect.equals("a_", forwardN(scope, 26));
+ // Now numbers.
+ Expect.equals("a0", forwardN(scope, 1));
+ Expect.equals("a9", forwardN(scope, 9));
+ // Make sure 'aa' is skipped on wrapping
+ Expect.equals("ab", forwardN(scope, 1));
+ Expect.equals("az", forwardN(scope, 24));
+}
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index c398750..d3968e2 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -64,45 +64,28 @@
21724_test: Crash # Please triage this failure.
[ $compiler == dart2js && $cps_ir ]
-11673_test: RuntimeError # Cannot read property 'prototype' of undefined
-12320_test: RuntimeError # Cannot read property 'prototype' of undefined
16407_test: Pass # Please triage this failure.
-22487_test: RuntimeError # Cannot read property 'prototype' of undefined
22868_test: Crash # (main()async{var clo... cannot handle async/sync*/async* functions
22895_test: Crash # (main()async{var clo... cannot handle async/sync*/async* functions
-23404_test: RuntimeError # Cannot read property 'prototype' of undefined
-23432_test : RuntimeError #
-LayoutTests_fast_mediastream_getusermedia_t01_test/none: RuntimeError # Cannot read property 'prototype' of undefined
+23432_test: RuntimeError # Please triage this failure.
async_stacktrace_test/asyncStar: Crash # (runTests()async{awa... cannot handle async/sync*/async* functions
async_stacktrace_test/none: Crash # (runTests()async{awa... cannot handle async/sync*/async* functions
-closure5_test: RuntimeError # Cannot read property 'prototype' of undefined
closure_capture5_test: Crash # (i=0): For-loop variable captured in loop header
-conditional_send_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred/deferred_class_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred/deferred_constant2_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred/deferred_constant3_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred/deferred_constant4_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred/deferred_function_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred/deferred_mirrors1_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred/deferred_mirrors2_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
-deferred/deferred_overlapping_test: RuntimeError # receiver.get$_collection$_nums is not a function
-for_test: RuntimeError # Please triage this failure.
-if_null_test: RuntimeError # receiver.get$_collection$_nums is not a function
-mirror_invalid_field_access3_test: Crash # Internal Error: No default constructor available.
-mirror_invalid_field_access_test: Crash # Internal Error: No default constructor available.
-mirror_invalid_invoke3_test: Crash # Internal Error: No default constructor available.
-mirror_invalid_invoke_test: Crash # Internal Error: No default constructor available.
-mirror_printer_test: Crash # Internal Error: No default constructor available.
-mirror_test: Crash # Internal Error: No default constructor available.
-mirror_type_inference_field2_test: Crash # Internal Error: No default constructor available.
-mirror_type_inference_field_test: Crash # Internal Error: No default constructor available.
-mirror_type_inference_function_test: Crash # Internal Error: No default constructor available.
-mirrors_used_warning2_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-mirrors_used_warning_test/minif : RuntimeError # TypeError: receiver.get$_nums is not a function
-mirrors_used_warning_test/none : RuntimeError # TypeError: receiver.get$_nums is not a function
-reflect_native_types_test: Crash # Internal Error: No default constructor available.
-runtime_type_test: RuntimeError # Cannot read property 'prototype' of undefined
+conditional_send_test: RuntimeError # receiver.get$_nums is not a function
+deferred/deferred_class_test: RuntimeError # receiver.get$_nums is not a function
+deferred/deferred_constant2_test: RuntimeError # receiver.get$_nums is not a function
+deferred/deferred_constant3_test: RuntimeError # receiver.get$_nums is not a function
+deferred/deferred_constant4_test: RuntimeError # receiver.get$_nums is not a function
+deferred/deferred_function_test: RuntimeError # receiver.get$_nums is not a function
+deferred/deferred_mirrors1_test: RuntimeError # receiver.get$_nums is not a function
+deferred/deferred_mirrors2_test: RuntimeError # receiver.get$_collection$_nums is not a function
+deferred/deferred_overlapping_test: RuntimeError # receiver.get$_nums is not a function
+if_null_test: RuntimeError # receiver.get$_nums is not a function
+mirror_printer_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirror_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors_used_warning2_test: RuntimeError # receiver.get$_nums is not a function
+mirrors_used_warning_test/minif: RuntimeError # receiver.get$_nums is not a function
+mirrors_used_warning_test/none: RuntimeError # receiver.get$_nums is not a function
+reflect_native_types_test: Crash # (=_EmptyStream<T>;): Unhandled node
switch_test/none: Crash # (switch (val){foo:ba... continue to a labeled switch case
-timer_test: RuntimeError # receiver.get$_collection$_nums is not a function
-no_such_method_test : Crash # Internal Error: No default constructor available.
-mirrors_used_closure_test : Crash # Internal Error: No default constructor available.
+timer_test: RuntimeError # receiver.get$_nums is not a function
diff --git a/tests/compiler/dart2js_native/dart2js_native.status b/tests/compiler/dart2js_native/dart2js_native.status
index bb3bb85..2b3774c 100644
--- a/tests/compiler/dart2js_native/dart2js_native.status
+++ b/tests/compiler/dart2js_native/dart2js_native.status
@@ -21,16 +21,15 @@
compute_this_script_test: Skip # Issue 17458
[ $compiler == dart2js && $cps_ir ]
-compute_this_script_test: RuntimeError # receiver.get$_collection$_nums is not a function
-event_loop_test: RuntimeError # receiver.get$_collection$_nums is not a function
-internal_library_test: RuntimeError # receiver.get$_collection$_nums is not a function
-mirror_intercepted_field_test: Crash # Internal Error: No default constructor available.
+compute_this_script_test: RuntimeError # receiver.get$_nums is not a function
+event_loop_test: RuntimeError # receiver.get$_nums is not a function
+internal_library_test: RuntimeError # receiver.get$_nums is not a function
+mirror_intercepted_field_test: Crash # (=_EmptyStream<T>;): Unhandled node
native_closure_identity_frog_test: RuntimeError # invoke is not a function
native_exception_test: RuntimeError # J.getInterceptor(...).toString$0 is not a function
native_method_inlining_test: RuntimeError # Please triage this failure.
-native_mirror_test: Crash # Internal Error: No default constructor available.
-native_mixin_field_test: RuntimeError # Please triage this failure.
-native_no_such_method_exception3_frog_test : RuntimeError #
+native_mirror_test: Crash # (=_EmptyStream<T>;): Unhandled node
+native_no_such_method_exception3_frog_test: RuntimeError # Please triage this failure.
native_wrapping_function3_frog_test: RuntimeError # invoke is not a function
native_wrapping_function_frog_test: RuntimeError # invoke is not a function
optimization_hints_test: RuntimeError # Please triage this failure.
diff --git a/tests/corelib/big_integer_arith_vm_test.dart b/tests/corelib/big_integer_arith_vm_test.dart
index 8b76ba0..49e6e0d 100644
--- a/tests/corelib/big_integer_arith_vm_test.dart
+++ b/tests/corelib/big_integer_arith_vm_test.dart
@@ -231,7 +231,7 @@
Expect.equals(0, x.modInverse(m));
x = 0;
m = 1000000001;
- Expect.throws(() => x.modInverse(m), (e) => e is RangeError); // Not coprime.
+ Expect.throws(() => x.modInverse(m), (e) => e is Exception); // Not coprime.
x = 1234567890;
m = 19;
Expect.equals(11, x.modInverse(m));
@@ -240,7 +240,7 @@
Expect.equals(189108911, x.modInverse(m));
x = 19;
m = 1000000001;
- Expect.throws(() => x.modInverse(m), (e) => e is RangeError); // Not coprime.
+ Expect.throws(() => x.modInverse(m), (e) => e is Exception); // Not coprime.
x = 19;
m = 1234567890;
Expect.equals(519818059, x.modInverse(m));
@@ -249,7 +249,7 @@
Expect.equals(1001100101, x.modInverse(m));
x = 1000000001;
m = 19;
- Expect.throws(() => x.modInverse(m), (e) => e is RangeError); // Not coprime.
+ Expect.throws(() => x.modInverse(m), (e) => e is Exception); // Not coprime.
x = 12345678901234567890;
m = 19;
Expect.equals(3, x.modInverse(m));
@@ -276,7 +276,7 @@
Expect.equals(3, x.modInverse(m));
x = 123456789012345678901234567890;
m = 123456789012345678901234567899;
- Expect.throws(() => x.modInverse(m), (e) => e is RangeError); // Not coprime.
+ Expect.throws(() => x.modInverse(m), (e) => e is Exception); // Not coprime.
x = 123456789012345678901234567890;
m = 123456789012345678901234567891;
Expect.equals(123456789012345678901234567890, x.modInverse(m));
@@ -285,7 +285,7 @@
Expect.equals(77160493132716049313271604932, x.modInverse(m));
x = 123456789012345678901234567899;
m = 123456789012345678901234567890;
- Expect.throws(() => x.modInverse(m), (e) => e is RangeError); // Not coprime.
+ Expect.throws(() => x.modInverse(m), (e) => e is Exception); // Not coprime.
x = 123456789012345678901234567891;
m = 123456789012345678901234567890;
Expect.equals(1, x.modInverse(m));
@@ -310,10 +310,31 @@
Expect.equals(21 <<40, x.gcd(m));
x = 0;
m = 1000000001;
- Expect.throws(() => x.gcd(m), (e) => e is RangeError);
+ Expect.equals(m, x.gcd(m));
x = 1000000001;
m = 0;
- Expect.throws(() => x.gcd(m), (e) => e is RangeError);
+ Expect.equals(x, x.gcd(m));
+ x = 0;
+ m = -1000000001;
+ Expect.equals(-m, x.gcd(m));
+ x = -1000000001;
+ m = 0;
+ Expect.equals(-x, x.gcd(m));
+ x = 0;
+ m = 0;
+ Expect.equals(0, x.gcd(m));
+ x = 0;
+ m = 123456789012345678901234567890;
+ Expect.equals(m, x.gcd(m));
+ x = 123456789012345678901234567890;
+ m = 0;
+ Expect.equals(x, x.gcd(m));
+ x = 0;
+ m = -123456789012345678901234567890;
+ Expect.equals(-m, x.gcd(m));
+ x = -123456789012345678901234567890;
+ m = 0;
+ Expect.equals(-x, x.gcd(m));
x = 1234567890;
m = 19;
Expect.equals(1, x.gcd(m));
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 693a690..4f9274e 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -213,47 +213,26 @@
big_integer_parsed_mul_div_vm_test: Pass, Slow
[ $compiler == dart2js && $cps_ir ]
-date_time2_test: RuntimeError # Cannot read property 'prototype' of undefined
-date_time3_test: RuntimeError # Cannot read property 'prototype' of undefined
-date_time4_test: RuntimeError # Cannot read property 'prototype' of undefined
-date_time7_test: RuntimeError # Cannot read property 'prototype' of undefined
-date_time_parse_test: RuntimeError # Cannot read property 'prototype' of undefined
error_stack_trace1_test: Pass # H.unwrapException(...).get$stackTrace is not a function
hashcode_test: RuntimeError # Please triage this failure.
-int_parse_radix_test/01: Crash # Invalid argument(s)
-int_parse_radix_test/02: Crash # Invalid argument(s)
iterable_empty_test: RuntimeError # Please triage this failure.
iterable_return_type_test/none: RuntimeError # Please triage this failure.
iterable_to_list_test: RuntimeError # Please triage this failure.
iterable_to_set_test: RuntimeError # Please triage this failure.
list_test/01: RuntimeError # this.get$length is not a function
list_test/none: RuntimeError # this.get$length is not a function
-main_test: RuntimeError # receiver.get$_collection$_nums is not a function
+main_test: RuntimeError # receiver.get$_nums is not a function
map_values2_test: RuntimeError # Please triage this failure.
map_values3_test: RuntimeError # Please triage this failure.
map_values4_test: RuntimeError # Please triage this failure.
-null_test: RuntimeError # Cannot read property 'prototype' of undefined
-reg_exp_first_match_test: RuntimeError # Cannot read property 'prototype' of undefined
-reg_exp_group_test: RuntimeError # Cannot read property 'prototype' of undefined
-reg_exp_string_match_test: RuntimeError # Cannot read property 'prototype' of undefined
-regexp/capture-3_test: RuntimeError # Cannot read property 'prototype' of undefined
-regexp/extended-characters-match_test: RuntimeError # Cannot read property 'prototype' of undefined
-regexp/non-bmp_test: RuntimeError # Cannot read property 'prototype' of undefined
regexp/pcre_test: Crash # Stack Overflow
-regexp/regress-regexp-construct-result_test: RuntimeError # Cannot read property 'prototype' of undefined
-regexp/results-cache_test: RuntimeError # Cannot read property 'prototype' of undefined
-regexp/stack-overflow_test: RuntimeError # Cannot read property 'prototype' of undefined
-regexp/unicode-handling_test: RuntimeError # Cannot read property 'prototype' of undefined
-shuffle_test: RuntimeError # Please triage this failure.
-stacktrace_fromstring_test: RuntimeError # receiver.get$_collection$_nums is not a function
-stopwatch2_test: RuntimeError # Cannot read property 'prototype' of undefined
-stopwatch_test: RuntimeError # Cannot read property 'prototype' of undefined
-string_fromcharcodes_test: RuntimeError # Please triage this failure.
-string_replace_all_test: RuntimeError # Cannot read property 'prototype' of undefined
-symbol_operator_test/03 : RuntimeError #
+shuffle_test: RuntimeError # this.get$length is not a function
+stacktrace_fromstring_test: RuntimeError # receiver.get$_nums is not a function
+string_fromcharcodes_test: RuntimeError # this.get$length is not a function
+symbol_operator_test/03: RuntimeError # Please triage this failure.
symbol_reserved_word_test/03: Pass # Please triage this failure.
symbol_reserved_word_test/06: RuntimeError # Please triage this failure.
symbol_reserved_word_test/09: RuntimeError # Please triage this failure.
symbol_reserved_word_test/12: RuntimeError # Please triage this failure.
symbol_test/none: Crash # The null object does not have a getter '_element'.
-uri_test: Crash # Invalid argument(s)
+uri_test: Crash # The null object does not have a getter '_element'.
diff --git a/tests/corelib/int_modulo_arith_test.dart b/tests/corelib/int_modulo_arith_test.dart
index 87d253d..9d5b9eb 100644
--- a/tests/corelib/int_modulo_arith_test.dart
+++ b/tests/corelib/int_modulo_arith_test.dart
@@ -141,14 +141,15 @@
// Test that gcd of value and other (non-negative) is expectedResult.
// Tests all combinations of positive and negative values and order of
// operands, so use positive values and order is not important.
- test(value, other, [expectedResult]) {
- assert(value % expectedResult == 0); // Check for bug in test.
- assert(other % expectedResult == 0);
+ test(value, other, expectedResult) {
+ // Check for bug in test.
+ assert(expectedResult == 0 || value % expectedResult == 0);
+ assert(expectedResult == 0 || other % expectedResult == 0);
callCombos(value, other, (a, b) {
var result = a.gcd(b);
/// Check that the result is a divisor.
- Expect.equals(0, a % result, "$result | $a");
- Expect.equals(0, b % result, "$result | $b");
+ Expect.equals(0, result == 0 ? a : a % result, "$result | $a");
+ Expect.equals(0, result == 0 ? b : b % result, "$result | $b");
// Check for bug in test. If assert fails, the expected value is too low,
// and the gcd call has found a greater common divisor.
assert(result >= expectedResult);
@@ -163,9 +164,8 @@
});
}
- // Throws if either operand is zero, and if both operands are zero.
- testThrows(0, 1000);
- testThrows(0, 0);
+ testThrows(2.5, 5); // Not a method on double.
+ testThrows(5, 2.5); // Not accepting non-int arguments.
// Format:
// test(value1, value2, expectedResult);
@@ -176,6 +176,9 @@
test(9999, 7272, 909); // Larger numbers
+ test(0, 1000, 1000); // One operand is zero.
+ test(0, 0, 0); // Both operands are zero.
+
// Multiplying both operands by a number multiplies result by same number.
test(693, 609, 21);
test(693 << 5, 609 << 5, 21 << 5);
diff --git a/tests/html/html.status b/tests/html/html.status
index 1dd6339..27ea36a 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -416,4 +416,3 @@
webgl_1_test: StaticWarning
window_nosuchmethod_test: StaticWarning
-[ $compiler == dart2js && $cps_ir ]
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index e6b89b8..2472eb6 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -129,54 +129,52 @@
bool_from_environment_default_value_test: RuntimeError # receiver.get$_collection$_nums is not a function
capability_test: RuntimeError # receiver.get$_collection$_nums is not a function
compile_time_error_test/none: RuntimeError # receiver.get$_collection$_nums is not a function
-count_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-cross_isolate_message_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-deferred_in_isolate2_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
+count_test: RuntimeError # receiver.get$_nums is not a function
+cross_isolate_message_test: RuntimeError # receiver.get$_nums is not a function
+deferred_in_isolate2_test: RuntimeError # receiver.get$_collection$_nums is not a function
deferred_in_isolate_test: RuntimeError # receiver.get$_collection$_nums is not a function
function_send_test: RuntimeError # receiver.get$_nums is not a function
handle_error2_test: RuntimeError # receiver.get$_collection$_nums is not a function
handle_error3_test: RuntimeError # receiver.get$_collection$_nums is not a function
handle_error_test: RuntimeError # receiver.get$_collection$_nums is not a function
-illegal_msg_function_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-illegal_msg_mirror_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+illegal_msg_function_test: RuntimeError # receiver.get$_nums is not a function
+illegal_msg_mirror_test: RuntimeError # receiver.get$_nums is not a function
int_from_environment_default_value_test: RuntimeError # receiver.get$_collection$_nums is not a function
-isolate_complex_messages_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+isolate_complex_messages_test: RuntimeError # receiver.get$_nums is not a function
isolate_current_test: RuntimeError # receiver.get$_nums is not a function
isolate_import_test/none: RuntimeError # receiver.get$_collection$_nums is not a function
issue_22778_test: RuntimeError # receiver.get$_collection$_nums is not a function
kill2_test: RuntimeError # receiver.get$_collection$_nums is not a function
kill_self_test: RuntimeError # receiver.get$_collection$_nums is not a function
kill_test: RuntimeError # receiver.get$_collection$_nums is not a function
-mandel_isolate_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
-message2_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+mandel_isolate_test: RuntimeError # receiver.get$_collection$_nums is not a function
+message2_test: RuntimeError # receiver.get$_nums is not a function
message3_test/byteBuffer: RuntimeError # receiver.get$_collection$_nums is not a function
-message3_test/constInstance: Crash # Invalid argument(s)
message3_test/fun: RuntimeError # receiver.get$_collection$_nums is not a function
message3_test/int32x4: RuntimeError # receiver.get$_collection$_nums is not a function
message3_test/none: RuntimeError # receiver.get$_collection$_nums is not a function
message_enum_test: RuntimeError # receiver.get$_collection$_nums is not a function
-message_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-mint_maker_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-nested_spawn2_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-nested_spawn_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+message_test: RuntimeError # receiver.get$_nums is not a function
+mint_maker_test: RuntimeError # receiver.get$_nums is not a function
+nested_spawn2_test: RuntimeError # receiver.get$_nums is not a function
+nested_spawn_test: RuntimeError # receiver.get$_nums is not a function
object_leak_test: RuntimeError # receiver.get$_collection$_nums is not a function
ondone_test: RuntimeError # receiver.get$_nums is not a function
pause_test: RuntimeError # receiver.get$_nums is not a function
ping_pause_test: RuntimeError # receiver.get$_collection$_nums is not a function
ping_test: RuntimeError # receiver.get$_collection$_nums is not a function
port_test: RuntimeError # receiver.get$_collection$_nums is not a function
-raw_port_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-request_reply_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+raw_port_test: RuntimeError # receiver.get$_nums is not a function
+request_reply_test: RuntimeError # receiver.get$_nums is not a function
simple_message_test/none: RuntimeError # receiver.get$_collection$_nums is not a function
-spawn_function_custom_class_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-spawn_function_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-spawn_uri_missing_from_isolate_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
-spawn_uri_missing_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
-spawn_uri_multi_test/none: Crash # Invalid argument(s)
-stacktrace_message_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+spawn_function_custom_class_test: RuntimeError # receiver.get$_nums is not a function
+spawn_function_test: RuntimeError # receiver.get$_nums is not a function
+spawn_uri_missing_from_isolate_test: RuntimeError # receiver.get$_collection$_nums is not a function
+spawn_uri_missing_test: RuntimeError # receiver.get$_collection$_nums is not a function
+stacktrace_message_test: RuntimeError # receiver.get$_nums is not a function
start_paused_test: RuntimeError # receiver.get$_nums is not a function
-static_function_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+static_function_test: RuntimeError # receiver.get$_nums is not a function
string_from_environment_default_value_test: RuntimeError # receiver.get$_collection$_nums is not a function
-timer_isolate_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
+timer_isolate_test: RuntimeError # receiver.get$_collection$_nums is not a function
typed_message_test: RuntimeError # receiver.get$_nums is not a function
-unresolved_ports_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+unresolved_ports_test: RuntimeError # receiver.get$_nums is not a function
diff --git a/tests/language/closure_self_reference_test.dart b/tests/language/closure_self_reference_test.dart
index 9ffc951..a47b1bf 100644
--- a/tests/language/closure_self_reference_test.dart
+++ b/tests/language/closure_self_reference_test.dart
@@ -18,6 +18,6 @@
}
}
- Expect.equals(0, inner(499));
- Expect.equals(499, counter);
+ Expect.equals(0, inner(199));
+ Expect.equals(199, counter);
}
diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status
index 5ced80a..d000c34 100644
--- a/tests/language/language_analyzer.status
+++ b/tests/language/language_analyzer.status
@@ -12,6 +12,7 @@
await_test: CompileTimeError # Issue 22052
async_await_test/02: CompileTimeError # Issue 22052
regress_17382_test: Skip # don't care about the static warning.
+regress_23408_test: Skip # don't care about the warning.
regress_23038_test/01: Skip # Issue 23038
getter_setter_in_lib_test: Fail # issue 23286
diff --git a/tests/language/language_analyzer2.status b/tests/language/language_analyzer2.status
index b35f11c..fbd47b1 100644
--- a/tests/language/language_analyzer2.status
+++ b/tests/language/language_analyzer2.status
@@ -14,6 +14,7 @@
enum_syntax_test/06: Fail # 21649
regress_17382_test: Skip # don't care about the static warning.
+regress_23408_test: Skip # don't care about the static warning.
getter_setter_in_lib_test: Fail # issue 23286
# Test issue 12694 (was analyzer issue), (1) when "abstract" is import prefix using it as type is warning; (2) currently analyzer resolves prefix as field (don't ask)
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 7b73b20..f728a01 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -259,6 +259,7 @@
async_await_syntax_test/a04c: Crash # (a04c()sync*{}): cannot handle async/sync*/async* functions
async_await_syntax_test/a05a: Crash # (a05a()async{await 0;}): cannot handle async/sync*/async* functions
async_await_syntax_test/a05b: Crash # (a05b()async{await (a){};await (0);}): cannot handle async/sync*/async* functions
+async_await_syntax_test/a05h: Crash # (a05h()async{yield*st;}): cannot handle async/sync*/async* functions
async_await_syntax_test/a06a: Crash # (a06a()async{await for(var o in st){}}): cannot handle async/sync*/async* functions
async_await_syntax_test/a07a: Crash # (a07a()sync*{yield 0;}): cannot handle async/sync*/async* functions
async_await_syntax_test/a08a: Crash # (a08a()sync*{yield* [] ;}): cannot handle async/sync*/async* functions
@@ -292,6 +293,8 @@
async_await_syntax_test/c08a: Crash # (c08a()sync*{yield* [] ;}): cannot handle async/sync*/async* functions
async_await_syntax_test/c09a: Crash # (c09a()async*{yield 0;}): cannot handle async/sync*/async* functions
async_await_syntax_test/c10a: Crash # (c10a()async*{yield* [] ;}): cannot handle async/sync*/async* functions
+async_await_syntax_test/c11a: Crash # (c11a()async{yield-5;}): cannot handle async/sync*/async* functions
+async_await_syntax_test/c11b: Crash # (c11b()async{yield*st;}): cannot handle async/sync*/async* functions
async_await_syntax_test/d01a: Crash # (()async=>null): cannot handle async/sync*/async* functions
async_await_syntax_test/d02a: Crash # (()async{}): cannot handle async/sync*/async* functions
async_await_syntax_test/d03a: Crash # (()async*{}): cannot handle async/sync*/async* functions
@@ -357,72 +360,56 @@
await_postfix_expr_test: Crash # (test()async{Expect.... cannot handle async/sync*/async* functions
await_regression_test: Crash # (main()async{testNes... cannot handle async/sync*/async* functions
await_test: Crash # (others()async{var a... cannot handle async/sync*/async* functions
-bind_test: RuntimeError # Cannot read property 'prototype' of undefined
-canonical_const3_test: RuntimeError # Cannot read property 'prototype' of undefined
-cascade_test/none: RuntimeError # Cannot read property 'prototype' of undefined
-cha_deopt1_test: RuntimeError # receiver.get$_collection$_nums is not a function
-cha_deopt2_test: RuntimeError # receiver.get$_collection$_nums is not a function
-cha_deopt3_test: RuntimeError # receiver.get$_collection$_nums is not a function
-closure7_test: RuntimeError # Cannot read property 'prototype' of undefined
-closure8_test: RuntimeError # Cannot read property 'prototype' of undefined
-closure_cycles_test: RuntimeError # receiver.get$_collection$_nums is not a function
+cha_deopt1_test: RuntimeError # receiver.get$_nums is not a function
+cha_deopt2_test: RuntimeError # receiver.get$_nums is not a function
+cha_deopt3_test: RuntimeError # receiver.get$_nums is not a function
+closure_cycles_test: RuntimeError # receiver.get$_nums is not a function
closure_in_constructor_test: Crash # Invalid argument(s)
-closure_shared_state_test: RuntimeError # Cannot read property 'prototype' of undefined
closure_type_variables_test: Crash # Invalid argument(s)
-closures_initializer2_test: RuntimeError # Cannot read property 'prototype' of undefined
-const_evaluation_test/01: Crash # Internal Error: No default constructor available.
+const_evaluation_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
constructor12_test: RuntimeError # Please triage this failure.
crash_6725_test/01: Crash # The null object does not have a getter '_element'.
custom_await_stack_trace_test: Crash # (main()async{try {va... cannot handle async/sync*/async* functions
-cyclic_type_test/00: RuntimeError # Cannot read property 'prototype' of undefined
-cyclic_type_test/01: RuntimeError # Cannot read property 'prototype' of undefined
-cyclic_type_test/02: RuntimeError # Cannot read property 'prototype' of undefined
-cyclic_type_test/03: RuntimeError # Cannot read property 'prototype' of undefined
-cyclic_type_test/04: RuntimeError # Cannot read property 'prototype' of undefined
-deferred_closurize_load_library_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constant_list_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_constants_test/none: Crash # Internal Error: No default constructor available.
-deferred_constraints_constants_test/reference_after_load: Crash # Internal Error: No default constructor available.
-deferred_constraints_type_annotation_test/as_operation: RuntimeError # receiver.get$_collection$_nums is not a function
+deferred_closurize_load_library_test: RuntimeError # receiver.get$_nums is not a function
+deferred_constant_list_test: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_constants_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+deferred_constraints_constants_test/reference_after_load: Crash # (=_EmptyStream<T>;): Unhandled node
+deferred_constraints_type_annotation_test/as_operation: RuntimeError # receiver.get$_nums is not a function
deferred_constraints_type_annotation_test/catch_check: Crash # The null object does not have a getter '_element'.
-deferred_constraints_type_annotation_test/is_check: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/new: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/new_before_load: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/new_generic1: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/new_generic2: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/new_generic3: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/none: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/static_method: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation1: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation_generic1: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation_generic2: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation_generic3: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation_generic4: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation_non_deferred: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation_null: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_constraints_type_annotation_test/type_annotation_top_level: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_function_type_test: RuntimeError # receiver.get$_collection$_nums is not a function
+deferred_constraints_type_annotation_test/is_check: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/new: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/new_before_load: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/new_generic1: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/new_generic2: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/new_generic3: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/none: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/static_method: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation1: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation_generic1: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation_generic2: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation_generic3: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation_generic4: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation_non_deferred: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation_null: RuntimeError # receiver.get$_nums is not a function
+deferred_constraints_type_annotation_test/type_annotation_top_level: RuntimeError # receiver.get$_nums is not a function
+deferred_function_type_test: RuntimeError # receiver.get$_nums is not a function
deferred_global_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_inlined_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_load_constants_test/none: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_load_inval_code_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_load_library_wrong_args_test/none: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_mixin_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_no_such_method_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+deferred_inlined_test: RuntimeError # receiver.get$_nums is not a function
+deferred_load_constants_test/none: RuntimeError # receiver.get$_nums is not a function
+deferred_load_inval_code_test: RuntimeError # receiver.get$_nums is not a function
+deferred_load_library_wrong_args_test/none: RuntimeError # receiver.get$_nums is not a function
+deferred_mixin_test: RuntimeError # receiver.get$_nums is not a function
+deferred_no_such_method_test: RuntimeError # receiver.get$_nums is not a function
deferred_not_loaded_check_test: RuntimeError # Please triage this failure.
-deferred_only_constant_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_optimized_test: RuntimeError # receiver.get$_collection$_nums is not a function
+deferred_only_constant_test: RuntimeError # receiver.get$_nums is not a function
+deferred_optimized_test: RuntimeError # receiver.get$_nums is not a function
deferred_redirecting_factory_test: Crash # (test()async{await t... cannot handle async/sync*/async* functions
-deferred_regression_22995_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_shadow_load_library_test: RuntimeError # receiver.get$_collection$_nums is not a function
-deferred_shared_and_unshared_classes_test: RuntimeError # receiver.get$_collection$_nums is not a function
+deferred_regression_22995_test: RuntimeError # receiver.get$_nums is not a function
+deferred_shadow_load_library_test: RuntimeError # receiver.get$_nums is not a function
+deferred_shared_and_unshared_classes_test: RuntimeError # receiver.get$_nums is not a function
deferred_static_seperate_test: RuntimeError # receiver.get$_collection$_nums is not a function
-enum_mirror_test: Crash # Internal Error: No default constructor available.
-f_bounded_equality_test: RuntimeError # Cannot read property 'prototype' of undefined
-final_super_field_set_test/01 : RuntimeError #
-fixed_type_variable_test/02: RuntimeError # Cannot read property 'prototype' of undefined
-fixed_type_variable_test/04: RuntimeError # Cannot read property 'prototype' of undefined
-fixed_type_variable_test/06: RuntimeError # Cannot read property 'prototype' of undefined
+enum_mirror_test: Crash # (=_EmptyStream<T>;): Unhandled node
+final_super_field_set_test/01: RuntimeError # Please triage this failure.
flatten_test/01: Crash # (test()async{int x=await new Derived<int>();}): cannot handle async/sync*/async* functions
flatten_test/02: Crash # (test()async{Future<int> f()async=>new Derived<int>();}): cannot handle async/sync*/async* functions
flatten_test/03: Crash # (test()async{Future<... cannot handle async/sync*/async* functions
@@ -438,110 +425,63 @@
flatten_test/none: Crash # (test()async{}): cannot handle async/sync*/async* functions
for2_test: Crash # The null object does not have a getter 'field'.
for_variable_capture_test: Crash # (i=0): For-loop variable captured in loop header
-function_getter_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_literals_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype0_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_bound_closure0_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_bound_closure1_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_call0_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_factory1_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_local0_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_local1_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_named1_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_not0_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_optional1_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_simple0_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_simple1_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_simple2_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_subtype_top_level0_test: RuntimeError # Cannot read property 'prototype' of undefined
-function_type_alias6_test/none: RuntimeError # Cannot read property 'prototype' of undefined
-generic_creation_test: RuntimeError # Cannot read property 'prototype' of undefined
generic_field_mixin3_test: RuntimeError # Please triage this failure.
-identical_closure_test: RuntimeError # Cannot read property 'prototype' of undefined
-if_null_assignment_static_test/01: RuntimeError # Issue 23669
-if_null_assignment_static_test/03: RuntimeError # Issue 23669
-if_null_assignment_static_test/04: RuntimeError # Issue 23669
-if_null_assignment_static_test/05: RuntimeError # Issue 23669
-if_null_assignment_static_test/36: RuntimeError # Issue 23669
-if_null_assignment_static_test/38: RuntimeError # Issue 23669
-if_null_assignment_static_test/39: RuntimeError # Issue 23669
-if_null_assignment_static_test/40: RuntimeError # Issue 23669
-implicit_closure1_test: RuntimeError # Cannot read property 'prototype' of undefined
-implicit_closure2_test: RuntimeError # Cannot read property 'prototype' of undefined
-implicit_closure_test: RuntimeError # Cannot read property 'prototype' of undefined
+if_null_assignment_static_test/01: RuntimeError # v0.get$a is not a function
+if_null_assignment_static_test/03: RuntimeError # v0.get$a is not a function
+if_null_assignment_static_test/04: RuntimeError # v0.get$b is not a function
+if_null_assignment_static_test/05: RuntimeError # v0.get$a is not a function
+if_null_assignment_static_test/36: RuntimeError # v0.get$a is not a function
+if_null_assignment_static_test/38: RuntimeError # v0.get$a is not a function
+if_null_assignment_static_test/39: RuntimeError # v0.get$b is not a function
+if_null_assignment_static_test/40: RuntimeError # v0.get$a is not a function
infinite_switch_label_test: Crash # (switch (target){l0:... continue to a labeled switch case
-inlined_conditional_test: RuntimeError # Cannot read property 'prototype' of undefined
-instance_creation_in_function_annotation_test: Crash # Internal Error: No default constructor available.
+instance_creation_in_function_annotation_test: Crash # (=_EmptyStream<T>;): Unhandled node
instanceof4_test/01: RuntimeError # Please triage this failure.
-invocation_mirror_invoke_on_test : RuntimeError #
+invocation_mirror_invoke_on_test: RuntimeError # Please triage this failure.
invocation_mirror_test: Crash # (super[37]=42): visitUnresolvedSuperIndexSet
-is_function_test: RuntimeError # Cannot read property 'prototype' of undefined
-issue13179_test: RuntimeError # Cannot read property 'prototype' of undefined
-issue_1751477_test: RuntimeError # receiver.get$_collection$_nums is not a function
+issue_1751477_test: RuntimeError # receiver.get$_nums is not a function
large_class_declaration_test: Crash # Stack Overflow
list_test: RuntimeError # Please triage this failure.
-main_test/01: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/02: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/04: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/05: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/20: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/21: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/22: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/41: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/42: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/43: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/44: RuntimeError # receiver.get$_collection$_nums is not a function
-main_test/45: RuntimeError # receiver.get$_collection$_nums is not a function
-many_overridden_no_such_method_test : RuntimeError #
-method_binding_test: RuntimeError # Cannot read property 'prototype' of undefined
-methods_as_constants2_test: RuntimeError # Cannot read property 'prototype' of undefined
-mint_compares_test: RuntimeError # Cannot read property 'prototype' of undefined
-mixin_super_constructor_named_test/01: Crash # Internal Error: No default constructor available.
-mixin_super_constructor_positionals_test/01: Crash # Internal Error: No default constructor available.
+main_test/01: RuntimeError # receiver.get$_nums is not a function
+main_test/02: RuntimeError # receiver.get$_nums is not a function
+main_test/04: RuntimeError # receiver.get$_nums is not a function
+main_test/05: RuntimeError # receiver.get$_nums is not a function
+main_test/20: RuntimeError # receiver.get$_nums is not a function
+main_test/21: RuntimeError # receiver.get$_nums is not a function
+main_test/22: RuntimeError # receiver.get$_nums is not a function
+main_test/41: RuntimeError # receiver.get$_nums is not a function
+main_test/42: RuntimeError # receiver.get$_nums is not a function
+main_test/43: RuntimeError # receiver.get$_nums is not a function
+main_test/44: RuntimeError # receiver.get$_nums is not a function
+main_test/45: RuntimeError # receiver.get$_nums is not a function
+many_overridden_no_such_method_test: RuntimeError # Please triage this failure.
mixin_type_parameters_mixin_extends_test: Crash # The null object does not have a getter '_element'.
mixin_type_parameters_mixin_test: Crash # The null object does not have a getter '_element'.
mixin_type_parameters_super_extends_test: Crash # The null object does not have a getter '_element'.
mixin_type_parameters_super_test: Crash # The null object does not have a getter '_element'.
-named_parameters_with_conversions_test: RuntimeError # Cannot read property 'prototype' of undefined
nested_switch_label_test: Crash # (switch (target){out... continue to a labeled switch case
-no_such_method_test : RuntimeError #
-null_test/none: Crash # Internal Error: No default constructor available.
-null_to_string2_test: RuntimeError # Cannot read property 'prototype' of undefined
-null_to_string_test: RuntimeError # Cannot read property 'prototype' of undefined
-overridden_no_such_method_test : RuntimeError #
-override_method_with_field_test/02: RuntimeError # Cannot read property 'prototype' of undefined
-override_method_with_field_test/none: RuntimeError # Cannot read property 'prototype' of undefined
-prefix14_test: RuntimeError # Cannot read property 'prototype' of undefined
-prefix15_test: RuntimeError # Cannot read property 'prototype' of undefined
-prefix21_test: RuntimeError # Cannot read property 'prototype' of undefined
-range_analysis_test: RuntimeError # Cannot read property 'prototype' of undefined
-recursive_calls_test: RuntimeError # Cannot read property 'prototype' of undefined
-reg_ex2_test: RuntimeError # Cannot read property 'prototype' of undefined
-reg_exp2_test: RuntimeError # Cannot read property 'prototype' of undefined
-regress_18535_test: Crash # Internal Error: No default constructor available.
+no_such_method_test: RuntimeError # Please triage this failure.
+null_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+overridden_no_such_method_test: RuntimeError # Please triage this failure.
+regress_18535_test: Crash # (=_EmptyStream<T>;): Unhandled node
regress_22438_test: Crash # (main()async{var err... cannot handle async/sync*/async* functions
-regress_22443_test: RuntimeError # receiver.get$_collection$_nums is not a function
+regress_22443_test: RuntimeError # receiver.get$_nums is not a function
regress_22445_test: Crash # (main()async{var err... cannot handle async/sync*/async* functions
regress_22579_test: Crash # (main()async{var err... cannot handle async/sync*/async* functions
regress_22728_test: Crash # (main()async{bool fa... cannot handle async/sync*/async* functions
regress_22777_test: Crash # (test()async{try {te... cannot handle async/sync*/async* functions
regress_22936_test/01: Crash # The null object does not have a getter '_element'.
regress_22936_test/none: Crash # The null object does not have a getter '_element'.
+regress_23408_test: RuntimeError # receiver.get$_nums is not a function
regress_23498_test: Crash # (main()async{var err... cannot handle async/sync*/async* functions
regress_23500_test/01: Crash # (main()async{var err... cannot handle async/sync*/async* functions
regress_23500_test/02: Crash # (main()async{var err... cannot handle async/sync*/async* functions
regress_23500_test/none: Crash # (main()async{var err... cannot handle async/sync*/async* functions
-static_closure_identical_test: RuntimeError # Cannot read property 'prototype' of undefined
-static_field_test/none: RuntimeError # Cannot read property 'prototype' of undefined
-static_implicit_closure_test: RuntimeError # Cannot read property 'prototype' of undefined
-string_interpolation_test/01: RuntimeError # Cannot read property 'prototype' of undefined
-string_interpolation_test/none: RuntimeError # Cannot read property 'prototype' of undefined
super_bound_closure_test/01: RuntimeError # Cannot read property 'call' of undefined
super_bound_closure_test/none: RuntimeError # Cannot read property 'call' of undefined
-super_call4_test : RuntimeError #
+super_call4_test: RuntimeError # Please triage this failure.
super_getter_setter_test: Crash # Class 'PartialMethodElement' has no instance getter 'initializer'.
-super_implicit_closure_test: RuntimeError # Cannot read property 'prototype' of undefined
-super_operator_index2_test: RuntimeError # this.get$map is not a function
+super_implicit_closure_test: RuntimeError # Cannot read property 'call' of undefined
super_operator_index5_test: Crash # (super[0]=42): visitUnresolvedSuperIndexSet
super_operator_index7_test: Crash # (super[0]=42): visitUnresolvedSuperIndexSet
super_operator_index8_test: Crash # (super[f()]=g()): visitUnresolvedSuperIndexSet
@@ -561,11 +501,7 @@
syncstar_yield_test/copyParameters: Crash # (Iterable<int> foo3(... cannot handle async/sync*/async* functions
syncstar_yield_test/none: Crash # (Iterable<int> foo3(... cannot handle async/sync*/async* functions
syncstar_yieldstar_test: Crash # (main()async{Expect.... cannot handle async/sync*/async* functions
-top_level_in_initializer_test: RuntimeError # Cannot read property 'prototype' of undefined
try_catch_test/none: Crash # The null object does not have a getter '_element'.
-type_check_const_function_typedef2_test/00: RuntimeError # Cannot read property 'prototype' of undefined
-type_check_const_function_typedef2_test/none: RuntimeError # Cannot read property 'prototype' of undefined
-type_check_const_function_typedef_test: RuntimeError # Cannot read property 'prototype' of undefined
type_parameter_test/01: Crash # Invalid argument(s)
type_parameter_test/02: Crash # Invalid argument(s)
type_parameter_test/03: Crash # Invalid argument(s)
@@ -573,26 +509,5 @@
type_parameter_test/05: Crash # Invalid argument(s)
type_parameter_test/06: Crash # Invalid argument(s)
type_parameter_test/none: Crash # Invalid argument(s)
-type_promotion_functions_test/01: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/02: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/03: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/04: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/05: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/06: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/07: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/08: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/09: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/10: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/11: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/12: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/13: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/14: RuntimeError # Cannot read property 'prototype' of undefined
-type_promotion_functions_test/none: RuntimeError # Cannot read property 'prototype' of undefined
type_variable_closure2_test: RuntimeError # Please triage this failure.
type_variable_closure_test: Crash # Invalid argument(s)
-typedef_is_test: RuntimeError # Cannot read property 'prototype' of undefined
-invocation_mirror2_test : Crash # Internal Error: No default constructor available.
-type_variable_conflict2_test/01 : Crash # Internal Error: No default constructor available.
-async_await_syntax_test/a05h : Crash # bailout: (a05h()async{yield*st;}): cannot handle async/sync*/async* functions
-async_await_syntax_test/c11a : Crash # bailout: (c11a()async{yield-5;}): cannot handle async/sync*/async* functions
-async_await_syntax_test/c11b : Crash # bailout: (c11b()async{yield*st;}): cannot handle async/sync*/async* functions
diff --git a/tests/language/regress_23408_lib.dart b/tests/language/regress_23408_lib.dart
new file mode 100644
index 0000000..64c4964
--- /dev/null
+++ b/tests/language/regress_23408_lib.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2015, 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.
+
+library regress_23408_lib;
+
+import "regress_23408_test.dart" as main;
+
+class K extends main.C {
+ K();
+}
diff --git a/tests/language/regress_23408_test.dart b/tests/language/regress_23408_test.dart
new file mode 100644
index 0000000..f47f1cb
--- /dev/null
+++ b/tests/language/regress_23408_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2015, 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.
+
+library regress_23408_test;
+
+import 'package:expect/expect.dart';
+
+import 'regress_23408_lib.dart' deferred as lib;
+
+class A<T> extends C {
+ get t => "$T";
+}
+
+class C {
+ var v = 55;
+ C();
+ factory C.c() = lib.K;
+ factory C.l() = A<lib.K>;
+}
+
+void main() {
+ var a = new C.l(); // Redirects to A<dynamic>
+ Expect.equals("dynamic", a.t);
+ Expect.throws(() => new C.c());
+ lib.loadLibrary().then((_) {
+ var b = new C.l(); // Still redirects to A<dynamic>
+ Expect.equals("dynamic", b.t);
+ var z = new C.c();
+ Expect.equals(55, z.v);
+ });
+}
diff --git a/tests/language/runtime_type_test.dart b/tests/language/runtime_type_test.dart
index 03c4576..158f974 100644
--- a/tests/language/runtime_type_test.dart
+++ b/tests/language/runtime_type_test.dart
@@ -10,4 +10,6 @@
main() {
Expect.isTrue(new A().className is Type);
+ Expect.isTrue(null.runtimeType is Type);
+ Expect.equals(null.runtimeType, Null);
}
diff --git a/tests/language/string_no_operator_test.dart b/tests/language/string_no_operator_test.dart
new file mode 100644
index 0000000..009442b
--- /dev/null
+++ b/tests/language/string_no_operator_test.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2015, 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 'package:expect/expect.dart';
+
+main() {
+ var x = "x";
+ var y = "y";
+ Expect.throws(() => x < y);
+ Expect.throws(() => x <= y);
+ Expect.throws(() => x > y);
+ Expect.throws(() => x >= y);
+ Expect.throws(() => x - y);
+ Expect.throws(() => x * y);
+ Expect.throws(() => x / y);
+ Expect.throws(() => x ~/ y);
+ Expect.throws(() => x % y);
+ Expect.throws(() => x >> y);
+ Expect.throws(() => x << y);
+ Expect.throws(() => x & y);
+ Expect.throws(() => x | y);
+ Expect.throws(() => x ^ y);
+ Expect.throws(() => -x);
+ Expect.throws(() => ~x);
+}
diff --git a/tests/lib/async/stream_iterator_test.dart b/tests/lib/async/stream_iterator_test.dart
index 57bd6cc..bbd7f71 100644
--- a/tests/lib/async/stream_iterator_test.dart
+++ b/tests/lib/async/stream_iterator_test.dart
@@ -6,89 +6,90 @@
import "package:unittest/unittest.dart";
main() {
- test("stream iterator basic", () {
- StreamController c = new StreamController();
- Stream s = c.stream;
- StreamIterator i = new StreamIterator(s);
- i.moveNext().then(expectAsync((bool b) {
- expect(b, isTrue);
- expect(42, i.current);
- return i.moveNext();
- })).then(expectAsync((bool b) {
- expect(b, isTrue);
- expect(37, i.current);
- return i.moveNext();
- })).then(expectAsync((bool b) {
- expect(b, isFalse);
- }));
- c.add(42);
- c.add(37);
- c.close();
+ test("stream iterator basic", () async {
+ var stream = createStream();
+ StreamIterator iterator = new StreamIterator(stream);
+ expect(iterator.current, isNull);
+ expect(await iterator.moveNext(), isTrue);
+ expect(iterator.current, 42);
+ expect(await iterator.moveNext(), isTrue);
+ expect(iterator.current, 37);
+ expect(await iterator.moveNext(), isFalse);
+ expect(iterator.current, isNull);
+ expect(await iterator.moveNext(), isFalse);
});
- test("stream iterator prefilled", () {
- StreamController c = new StreamController();
- c.add(42);
- c.add(37);
- c.close();
- Stream s = c.stream;
- StreamIterator i = new StreamIterator(s);
- i.moveNext().then(expectAsync((bool b) {
- expect(b, isTrue);
- expect(42, i.current);
- return i.moveNext();
- })).then(expectAsync((bool b) {
- expect(b, isTrue);
- expect(37, i.current);
- return i.moveNext();
- })).then(expectAsync((bool b) {
- expect(b, isFalse);
- }));
+ test("stream iterator prefilled", () async {
+ Stream stream = createStream();
+ StreamIterator iterator = new StreamIterator(stream);
+ await new Future.delayed(Duration.ZERO);
+ expect(iterator.current, isNull);
+ expect(await iterator.moveNext(), isTrue);
+ expect(iterator.current, 42);
+ expect(await iterator.moveNext(), isTrue);
+ expect(iterator.current, 37);
+ expect(await iterator.moveNext(), isFalse);
+ expect(iterator.current, isNull);
+ expect(await iterator.moveNext(), isFalse);
});
- test("stream iterator error", () {
- StreamController c = new StreamController();
- Stream s = c.stream;
- StreamIterator i = new StreamIterator(s);
- i.moveNext().then(expectAsync((bool b) {
- expect(b, isTrue);
- expect(42, i.current);
- return i.moveNext();
- })).then((bool b) {
- fail("Result not expected");
- }, onError: expectAsync((e) {
- expect("BAD", e);
- return i.moveNext();
- })).then(expectAsync((bool b) {
- expect(b, isFalse);
- }));
- c.add(42);
- c.addError("BAD");
- c.add(37);
- c.close();
+ test("stream iterator error", () async {
+ Stream stream = createErrorStream();
+ StreamIterator iterator = new StreamIterator(stream);
+ expect(await iterator.moveNext(), isTrue);
+ expect(iterator.current, 42);
+ var hasNext = iterator.moveNext();
+ expect(hasNext, throwsA("BAD")); // This is an async expectation,
+ await hasNext.catchError((_){}); // so we have to wait for the future too.
+ expect(iterator.current, isNull);
+ expect(await iterator.moveNext(), isFalse);
+ expect(iterator.current, isNull);
});
- test("stream iterator current/moveNext during move", () {
- StreamController c = new StreamController();
- Stream s = c.stream;
- StreamIterator i = new StreamIterator(s);
- i.moveNext().then(expectAsync((bool b) {
- expect(b, isTrue);
- expect(42, i.current);
- new Timer(const Duration(milliseconds:100), expectAsync(() {
- expect(i.current, null);
- expect(() { i.moveNext(); }, throws);
- c.add(37);
- c.close();
- }));
- return i.moveNext();
- })).then(expectAsync((bool b) {
- expect(b, isTrue);
- expect(37, i.current);
- return i.moveNext();
- })).then(expectAsync((bool b) {
- expect(b, isFalse);
- }));
- c.add(42);
+ test("stream iterator current/moveNext during move", () async {
+ Stream stream = createStream();
+ StreamIterator iterator = new StreamIterator(stream);
+ var hasNext = iterator.moveNext();
+ expect(iterator.moveNext, throwsA(isStateError));
+ expect(await hasNext, isTrue);
+ expect(iterator.current, 42);
+ iterator.cancel();
});
+
+ test("stream iterator error during cancel", () async {
+ Stream stream = createCancelErrorStream();
+ StreamIterator iterator = new StreamIterator(stream);
+ for (int i = 0; i < 10; i++) {
+ expect(await iterator.moveNext(), isTrue);
+ expect(iterator.current, i);
+ }
+ var hasNext = iterator.moveNext(); // active moveNext will be completed.
+ var cancel = iterator.cancel();
+ expect(cancel, throwsA("BAD"));
+ expect(await hasNext, isFalse);
+ expect(await iterator.moveNext(), isFalse);
+ });
+
+}
+
+Stream createStream() async* {
+ yield 42;
+ yield 37;
+}
+
+Stream createErrorStream() async* {
+ yield 42;
+ // Emit an error without stopping the generator.
+ yield* (new Future.error("BAD").asStream());
+ yield 37;
+}
+
+/// Create a stream that throws when cancelled.
+Stream createCancelErrorStream() async* {
+ int i = 0;
+ try {
+ while (true) yield i++;
+ } finally {
+ throw "BAD";
+ }
}
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 015e3bd..e63fadf 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -151,7 +151,6 @@
async/zone_empty_description2_test: RuntimeError # Timer interface not supported: Issue 7728.
async/zone_create_timer2_test: RuntimeError # Timer interface not supported: Issue 7728.
async/zone_create_periodic_timer_test: RuntimeError # Timer interface not supported: Issue 7728.
-async/stream_iterator_test: RuntimeError, OK # Timer interface not supported: Issue 7728.
async/catch_errors12_test: Fail # Timer interface not supported: Issue 7728.
async/catch_errors13_test: Fail # Timer interface not supported: Issue 7728.
async/catch_errors14_test: Fail # Timer interface not supported: Issue 7728.
@@ -354,18 +353,18 @@
async/catch_errors7_test: RuntimeError # receiver.get$_nums is not a function
async/catch_errors8_test: RuntimeError # receiver.get$_nums is not a function
async/catch_errors_test: RuntimeError # receiver.get$_nums is not a function
-async/first_regression_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/future_constructor_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/future_delayed_error_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/future_microtask_test: RuntimeError # receiver.get$_collection$_nums is not a function
+async/first_regression_test: RuntimeError # receiver.get$_nums is not a function
+async/future_constructor_test: RuntimeError # receiver.get$_nums is not a function
+async/future_delayed_error_test: RuntimeError # receiver.get$_nums is not a function
+async/future_microtask_test: RuntimeError # receiver.get$_nums is not a function
async/future_test/01: Crash # (()async=>new Future.value(value)): cannot handle async/sync*/async* functions
async/future_test/none: RuntimeError # receiver.get$_nums is not a function
-async/future_timeout_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/future_value_chain2_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/future_value_chain3_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/future_value_chain4_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/future_value_chain_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/futures_test: RuntimeError # receiver.get$_collection$_nums is not a function
+async/future_timeout_test: RuntimeError # receiver.get$_nums is not a function
+async/future_value_chain2_test: RuntimeError # receiver.get$_nums is not a function
+async/future_value_chain3_test: RuntimeError # receiver.get$_nums is not a function
+async/future_value_chain4_test: RuntimeError # receiver.get$_nums is not a function
+async/future_value_chain_test: RuntimeError # receiver.get$_nums is not a function
+async/futures_test: RuntimeError # receiver.get$_nums is not a function
async/intercept_print1_test: RuntimeError # receiver.get$_collection$_nums is not a function
async/intercept_schedule_microtask1_test: RuntimeError # receiver.get$_nums is not a function
async/intercept_schedule_microtask2_test: RuntimeError # receiver.get$_nums is not a function
@@ -373,7 +372,7 @@
async/intercept_schedule_microtask4_test: RuntimeError # receiver.get$_nums is not a function
async/intercept_schedule_microtask5_test: RuntimeError # receiver.get$_nums is not a function
async/intercept_schedule_microtask6_test: RuntimeError # receiver.get$_nums is not a function
-async/multiple_timer_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+async/multiple_timer_test: RuntimeError # receiver.get$_nums is not a function
async/print_test/none: RuntimeError # receiver.get$_nums is not a function
async/run_zoned1_test: RuntimeError # receiver.get$_nums is not a function
async/run_zoned4_test: RuntimeError # receiver.get$_nums is not a function
@@ -382,73 +381,73 @@
async/run_zoned7_test: RuntimeError # receiver.get$_nums is not a function
async/run_zoned8_test: RuntimeError # receiver.get$_nums is not a function
async/run_zoned9_test/none: RuntimeError # receiver.get$_nums is not a function
-async/schedule_microtask2_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/schedule_microtask3_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/schedule_microtask5_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/schedule_microtask_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/slow_consumer2_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/slow_consumer3_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/slow_consumer_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace01_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace02_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace03_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace04_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace05_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace06_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace07_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace08_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace09_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace10_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace11_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace12_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace13_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace14_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace15_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace16_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace17_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace18_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace19_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace20_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace21_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace22_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace23_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace24_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stack_trace25_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stream_controller_async_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_controller_test: RuntimeError # receiver.get$_collection$_nums is not a function
+async/schedule_microtask2_test: RuntimeError # receiver.get$_nums is not a function
+async/schedule_microtask3_test: RuntimeError # receiver.get$_nums is not a function
+async/schedule_microtask5_test: RuntimeError # receiver.get$_nums is not a function
+async/schedule_microtask_test: RuntimeError # receiver.get$_nums is not a function
+async/slow_consumer2_test: RuntimeError # receiver.get$_nums is not a function
+async/slow_consumer3_test: RuntimeError # receiver.get$_nums is not a function
+async/slow_consumer_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace01_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace02_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace03_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace04_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace05_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace06_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace07_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace08_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace09_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace10_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace11_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace12_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace13_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace14_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace15_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace16_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace17_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace18_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace19_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace20_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace21_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace22_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace23_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace24_test: RuntimeError # receiver.get$_nums is not a function
+async/stack_trace25_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_controller_async_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_controller_test: RuntimeError # receiver.get$_nums is not a function
async/stream_empty_test: Crash # (Future runTest()asy... cannot handle async/sync*/async* functions
async/stream_event_transformed_test: RuntimeError # receiver.get$_nums is not a function
-async/stream_first_where_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_from_iterable_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_iterator_double_cancel_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stream_iterator_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_join_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_last_where_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+async/stream_first_where_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_from_iterable_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_iterator_double_cancel_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_iterator_test: Crash # (()async{var stream=... cannot handle async/sync*/async* functions
+async/stream_join_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_last_where_test: RuntimeError # receiver.get$_nums is not a function
async/stream_listen_zone_test: RuntimeError # receiver.get$_nums is not a function
-async/stream_periodic2_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_periodic3_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_periodic4_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_periodic5_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_periodic_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_single_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_single_to_multi_subscriber_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_state_nonzero_timer_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_state_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_subscription_as_future_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_subscription_cancel_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_timeout_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_transform_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_transformation_broadcast_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/stream_transformer_from_handlers_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/stream_transformer_test: RuntimeError # receiver.get$_collection$_nums is not a function
+async/stream_periodic2_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_periodic3_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_periodic4_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_periodic5_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_periodic_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_single_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_single_to_multi_subscriber_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_state_nonzero_timer_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_state_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_subscription_as_future_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_subscription_cancel_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_timeout_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_transform_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_transformation_broadcast_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_transformer_from_handlers_test: RuntimeError # receiver.get$_nums is not a function
+async/stream_transformer_test: RuntimeError # receiver.get$_nums is not a function
async/stream_zones_test: RuntimeError # receiver.get$_nums is not a function
-async/timer_cancel1_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/timer_cancel2_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/timer_cancel_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/timer_isActive_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/timer_regress22626_test: RuntimeError # receiver.get$_collection$_nums is not a function
-async/timer_repeat_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-async/timer_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+async/timer_cancel1_test: RuntimeError # receiver.get$_nums is not a function
+async/timer_cancel2_test: RuntimeError # receiver.get$_nums is not a function
+async/timer_cancel_test: RuntimeError # receiver.get$_nums is not a function
+async/timer_isActive_test: RuntimeError # receiver.get$_nums is not a function
+async/timer_regress22626_test: RuntimeError # receiver.get$_nums is not a function
+async/timer_repeat_test: RuntimeError # receiver.get$_nums is not a function
+async/timer_test: RuntimeError # receiver.get$_nums is not a function
async/zone_bind_callback_test: RuntimeError # receiver.get$_nums is not a function
async/zone_bind_callback_unary_test: RuntimeError # receiver.get$_nums is not a function
async/zone_bind_test: RuntimeError # receiver.get$_nums is not a function
@@ -468,248 +467,240 @@
async/zone_run_test: RuntimeError # receiver.get$_nums is not a function
async/zone_run_unary_test: RuntimeError # receiver.get$_nums is not a function
async/zone_value_test: RuntimeError # receiver.get$_collection$_nums is not a function
-convert/ascii_test: RuntimeError # Please triage this failure.
-convert/chunked_conversion_utf88_test: RuntimeError # Please triage this failure.
-convert/codec1_test: RuntimeError # Cannot read property 'prototype' of undefined
-convert/encoding_test: RuntimeError # receiver.get$_collection$_nums is not a function
-convert/html_escape_test: RuntimeError # receiver.get$_collection$_nums is not a function
-convert/json_lib_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-convert/latin1_test: RuntimeError # Please triage this failure.
-convert/line_splitter_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+convert/ascii_test: RuntimeError # this.get$length is not a function
+convert/encoding_test: RuntimeError # receiver.get$_nums is not a function
+convert/html_escape_test: RuntimeError # receiver.get$_nums is not a function
+convert/json_lib_test: RuntimeError # receiver.get$_nums is not a function
+convert/latin1_test: RuntimeError # this.get$length is not a function
+convert/line_splitter_test: RuntimeError # receiver.get$_nums is not a function
convert/streamed_conversion_json_decode1_test: RuntimeError # receiver.get$_collection$_nums is not a function
-convert/streamed_conversion_json_encode1_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
+convert/streamed_conversion_json_encode1_test: RuntimeError # receiver.get$_collection$_nums is not a function
convert/streamed_conversion_json_utf8_decode_test: RuntimeError # receiver.get$_collection$_nums is not a function
-convert/streamed_conversion_json_utf8_encode_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
-convert/streamed_conversion_utf8_decode_test: RuntimeError # receiver.get$_collection$_nums is not a function
-convert/streamed_conversion_utf8_encode_test: RuntimeError # receiver.get$_collection$_nums is not a function
-math/pi_test: RuntimeError # receiver.get$_collection$_nums is not a function
-math/point_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-math/rectangle_test : RuntimeError # TypeError: receiver.get$_nums is not a function
-mirrors/abstract_class_test/00: Crash # Internal Error: No default constructor available.
-mirrors/abstract_class_test/none: Crash # Internal Error: No default constructor available.
-mirrors/abstract_test: Crash # Internal Error: No default constructor available.
-mirrors/accessor_cache_overflow_test: Crash # Internal Error: No default constructor available.
-mirrors/array_tracing2_test: Crash # Internal Error: No default constructor available.
-mirrors/array_tracing3_test: Crash # Internal Error: No default constructor available.
-mirrors/array_tracing_test: Crash # Internal Error: No default constructor available.
-mirrors/basic_types_in_dart_core_test: Crash # Internal Error: No default constructor available.
-mirrors/circular_factory_redirection_test/none: Crash # Internal Error: No default constructor available.
-mirrors/class_declarations_test/01: Crash # Internal Error: No default constructor available.
-mirrors/class_declarations_test/none: Crash # Internal Error: No default constructor available.
-mirrors/class_mirror_location_test: Crash # Internal Error: No default constructor available.
-mirrors/class_mirror_type_variables_test: Crash # Internal Error: No default constructor available.
-mirrors/closures_test: Crash # Internal Error: No default constructor available.
-mirrors/closurization_equivalence_test: Crash # Internal Error: No default constructor available.
-mirrors/constructor_kinds_test/01: Crash # Internal Error: No default constructor available.
-mirrors/constructor_kinds_test/none: Crash # Internal Error: No default constructor available.
-mirrors/constructors_test: Crash # Internal Error: No default constructor available.
-mirrors/dart2js_mirrors_test: Crash # Internal Error: No default constructor available.
-mirrors/declarations_type_test: Crash # Internal Error: No default constructor available.
-mirrors/deferred_mirrors_metadata_test: Crash # Internal Error: No default constructor available.
-mirrors/deferred_mirrors_metatarget_test: Crash # Internal Error: No default constructor available.
-mirrors/deferred_mirrors_update_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
-mirrors/deferred_type_test: Crash # Internal Error: No default constructor available.
-mirrors/delegate_call_through_getter_test : RuntimeError #
-mirrors/delegate_test : RuntimeError #
-mirrors/disable_tree_shaking_test: Crash # Internal Error: No default constructor available.
-mirrors/empty_test: Crash # Internal Error: No default constructor available.
-mirrors/enum_test: Crash # Internal Error: No default constructor available.
-mirrors/equality_test: Crash # Internal Error: No default constructor available.
-mirrors/fake_function_with_call_test: Crash # Internal Error: No default constructor available.
-mirrors/fake_function_without_call_test: Crash # Internal Error: No default constructor available.
-mirrors/field_type_test: Crash # Internal Error: No default constructor available.
-mirrors/function_type_mirror_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_bounded_by_type_parameter_test/01: Crash # Internal Error: No default constructor available.
-mirrors/generic_bounded_by_type_parameter_test/02: Crash # Internal Error: No default constructor available.
-mirrors/generic_bounded_by_type_parameter_test/none: Crash # Internal Error: No default constructor available.
-mirrors/generic_bounded_test/01: Crash # Internal Error: No default constructor available.
-mirrors/generic_bounded_test/02: Crash # Internal Error: No default constructor available.
-mirrors/generic_bounded_test/none: Crash # Internal Error: No default constructor available.
-mirrors/generic_class_declaration_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_f_bounded_mixin_application_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_f_bounded_test/01: Crash # Internal Error: No default constructor available.
-mirrors/generic_f_bounded_test/none: Crash # Internal Error: No default constructor available.
-mirrors/generic_function_typedef_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_interface_test/01: Crash # Internal Error: No default constructor available.
-mirrors/generic_interface_test/none: Crash # Internal Error: No default constructor available.
-mirrors/generic_list_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_local_function_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_mixin_applications_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_mixin_test: Crash # Internal Error: No default constructor available.
-mirrors/generic_superclass_test/01: Crash # Internal Error: No default constructor available.
-mirrors/generic_superclass_test/none: Crash # Internal Error: No default constructor available.
-mirrors/generic_type_mirror_test: Crash # Internal Error: No default constructor available.
-mirrors/generics_double_substitution_test/01: Crash # Internal Error: No default constructor available.
-mirrors/generics_double_substitution_test/none: Crash # Internal Error: No default constructor available.
-mirrors/generics_dynamic_test: Crash # Internal Error: No default constructor available.
-mirrors/generics_special_types_test: Crash # Internal Error: No default constructor available.
-mirrors/generics_substitution_test: Crash # Internal Error: No default constructor available.
-mirrors/generics_test/01: Crash # Internal Error: No default constructor available.
-mirrors/generics_test/none: Crash # Internal Error: No default constructor available.
-mirrors/get_field_static_test/00: Crash # Internal Error: No default constructor available.
-mirrors/get_field_static_test/none: Crash # Internal Error: No default constructor available.
-mirrors/globalized_closures2_test/00: Crash # Internal Error: No default constructor available.
-mirrors/globalized_closures2_test/none: Crash # Internal Error: No default constructor available.
-mirrors/globalized_closures_test/00: Crash # Internal Error: No default constructor available.
-mirrors/globalized_closures_test/none: Crash # Internal Error: No default constructor available.
-mirrors/hierarchy_invariants_test: Crash # Internal Error: No default constructor available.
-mirrors/immutable_collections_test: Crash # Internal Error: No default constructor available.
-mirrors/inherit_field_test: Crash # Internal Error: No default constructor available.
-mirrors/initializing_formals_test/01: Crash # Internal Error: No default constructor available.
-mirrors/initializing_formals_test/none: Crash # Internal Error: No default constructor available.
-mirrors/instance_members_easier_test: Crash # Internal Error: No default constructor available.
-mirrors/instance_members_test: Crash # Internal Error: No default constructor available.
-mirrors/instance_members_unimplemented_interface_test: Crash # Internal Error: No default constructor available.
-mirrors/instance_members_with_override_test: Crash # Internal Error: No default constructor available.
-mirrors/instantiate_abstract_class_test: Crash # Internal Error: No default constructor available.
-mirrors/intercepted_cache_test: Crash # Internal Error: No default constructor available.
-mirrors/intercepted_class_test: Crash # Internal Error: No default constructor available.
-mirrors/intercepted_object_test: Crash # Internal Error: No default constructor available.
-mirrors/intercepted_superclass_test: Crash # Internal Error: No default constructor available.
-mirrors/invocation_fuzz_test/emptyarray: Crash # Internal Error: No default constructor available.
-mirrors/invocation_fuzz_test/false: Crash # Internal Error: No default constructor available.
-mirrors/invocation_fuzz_test/none: Crash # Internal Error: No default constructor available.
-mirrors/invocation_fuzz_test/smi: Crash # Internal Error: No default constructor available.
-mirrors/invocation_fuzz_test/string: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_on_closure_test: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_through_getter_previously_accessed_test/named: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_through_getter_previously_accessed_test/none: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_through_getter_test/named: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_through_getter_test/none: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_through_implicit_getter_previously_accessed_test/named: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_through_implicit_getter_previously_accessed_test/none: Crash # Internal Error: No default constructor available.
-mirrors/invoke_call_through_implicit_getter_test: Crash # Internal Error: No default constructor available.
-mirrors/invoke_closurization2_test: Crash # Internal Error: No default constructor available.
-mirrors/invoke_closurization_test: Crash # Internal Error: No default constructor available.
-mirrors/invoke_import_test: Crash # Internal Error: No default constructor available.
-mirrors/invoke_named_test/01: Crash # Internal Error: No default constructor available.
-mirrors/invoke_named_test/none: Crash # Internal Error: No default constructor available.
-mirrors/invoke_natives_malicious_test: Crash # Internal Error: No default constructor available.
-mirrors/invoke_test: Crash # Internal Error: No default constructor available.
-mirrors/invoke_throws_test: Crash # Internal Error: No default constructor available.
-mirrors/is_odd_test: Crash # Internal Error: No default constructor available.
-mirrors/lazy_static_test: Crash # Internal Error: No default constructor available.
-mirrors/libraries_test: Crash # Internal Error: No default constructor available.
-mirrors/library_declarations_test/01: Crash # Internal Error: No default constructor available.
-mirrors/library_declarations_test/none: Crash # Internal Error: No default constructor available.
-mirrors/library_enumeration_deferred_loading_test: Crash # Internal Error: No default constructor available.
-mirrors/library_exports_hidden_test: Crash # Internal Error: No default constructor available.
-mirrors/library_exports_shown_test: Crash # Internal Error: No default constructor available.
-mirrors/library_import_deferred_loading_test: Crash # Internal Error: No default constructor available.
-mirrors/library_imports_bad_metadata_test/none: Crash # Internal Error: No default constructor available.
-mirrors/library_imports_deferred_test: Crash # Internal Error: No default constructor available.
-mirrors/library_imports_hidden_test: Crash # Internal Error: No default constructor available.
-mirrors/library_imports_metadata_test: Crash # Internal Error: No default constructor available.
-mirrors/library_imports_prefixed_show_hide_test: Crash # Internal Error: No default constructor available.
-mirrors/library_imports_prefixed_test: Crash # Internal Error: No default constructor available.
-mirrors/library_imports_shown_test: Crash # Internal Error: No default constructor available.
-mirrors/library_metadata2_test/none: Crash # Internal Error: No default constructor available.
-mirrors/library_metadata_test: Crash # Internal Error: No default constructor available.
-mirrors/library_uri_package_test: Crash # Invalid argument(s)
-mirrors/list_constructor_test/01: Crash # Internal Error: No default constructor available.
-mirrors/list_constructor_test/none: Crash # Internal Error: No default constructor available.
-mirrors/load_library_test: Crash # Internal Error: No default constructor available.
-mirrors/local_function_is_static_test: Crash # Internal Error: No default constructor available.
-mirrors/local_isolate_test: Crash # Internal Error: No default constructor available.
-mirrors/metadata_allowed_values_test/01: Crash # Internal Error: No default constructor available.
-mirrors/metadata_allowed_values_test/05: Crash # Internal Error: No default constructor available.
-mirrors/metadata_allowed_values_test/10: Crash # Internal Error: No default constructor available.
-mirrors/metadata_allowed_values_test/11: Crash # Internal Error: No default constructor available.
-mirrors/metadata_allowed_values_test/13: Crash # Internal Error: No default constructor available.
-mirrors/metadata_allowed_values_test/14: Crash # Internal Error: No default constructor available.
-mirrors/metadata_allowed_values_test/none: Crash # Internal Error: No default constructor available.
-mirrors/metadata_class_mirror_test: Crash # Internal Error: No default constructor available.
-mirrors/metadata_constructed_constant_test: Crash # Internal Error: No default constructor available.
-mirrors/metadata_constructor_arguments_test/none: Crash # Internal Error: No default constructor available.
-mirrors/metadata_nested_constructor_call_test/none: Crash # Internal Error: No default constructor available.
-mirrors/metadata_test: Crash # Internal Error: No default constructor available.
-mirrors/method_mirror_location_test: Crash # Internal Error: No default constructor available.
-mirrors/method_mirror_name_test: Crash # Internal Error: No default constructor available.
-mirrors/method_mirror_properties_test: Crash # Internal Error: No default constructor available.
-mirrors/method_mirror_returntype_test: Crash # Internal Error: No default constructor available.
-mirrors/method_mirror_source_line_ending_test: Crash # Internal Error: No default constructor available.
-mirrors/method_mirror_source_test: Crash # Internal Error: No default constructor available.
-mirrors/mirror_in_static_init_test/none: Crash # Internal Error: No default constructor available.
-mirrors/mirrors_nsm_mismatch_test: Crash # Internal Error: No default constructor available.
-mirrors/mirrors_nsm_test/dart2js: Crash # Internal Error: No default constructor available.
-mirrors/mirrors_nsm_test/none: Crash # Internal Error: No default constructor available.
-mirrors/mirrors_reader_test: Crash # Internal Error: No default constructor available.
-mirrors/mirrors_resolve_fields_test: Crash # Internal Error: No default constructor available.
-mirrors/mirrors_test: Crash # Internal Error: No default constructor available.
-mirrors/mixin_application_test: Crash # Internal Error: No default constructor available.
-mirrors/mixin_members_test: Crash # Internal Error: No default constructor available.
-mirrors/mixin_test: Crash # Internal Error: No default constructor available.
-mirrors/native_class_test: Crash # Internal Error: No default constructor available.
-mirrors/new_instance_optional_arguments_test: Crash # Internal Error: No default constructor available.
-mirrors/new_instance_with_type_arguments_test: Crash # Internal Error: No default constructor available.
-mirrors/no_metadata_test: Crash # Internal Error: No default constructor available.
-mirrors/null2_test: Crash # Internal Error: No default constructor available.
-mirrors/null_test: Crash # Invalid argument(s)
-mirrors/operator_test: Crash # Internal Error: No default constructor available.
-mirrors/parameter_is_const_test/none: Crash # Internal Error: No default constructor available.
-mirrors/parameter_metadata_test: Crash # Internal Error: No default constructor available.
-mirrors/parameter_of_mixin_app_constructor_test: Crash # Internal Error: No default constructor available.
-mirrors/parameter_test/none: Crash # Internal Error: No default constructor available.
-mirrors/private_symbol_mangling_test: Crash # Internal Error: No default constructor available.
-mirrors/private_types_test: Crash # Internal Error: No default constructor available.
-mirrors/proxy_type_test: Crash # Internal Error: No default constructor available.
-mirrors/raw_type_test/01: Crash # Internal Error: No default constructor available.
-mirrors/raw_type_test/none: Crash # Internal Error: No default constructor available.
-mirrors/redirecting_factory_test/01: Crash # Internal Error: No default constructor available.
-mirrors/redirecting_factory_test/02: Crash # Internal Error: No default constructor available.
-mirrors/redirecting_factory_test/none: Crash # Internal Error: No default constructor available.
-mirrors/reflect_class_test/01: Crash # Internal Error: No default constructor available.
-mirrors/reflect_class_test/02: Crash # Internal Error: No default constructor available.
-mirrors/reflect_class_test/none: Crash # Internal Error: No default constructor available.
-mirrors/reflect_model_test: Crash # Internal Error: No default constructor available.
-mirrors/reflect_runtime_type_test: Crash # Internal Error: No default constructor available.
-mirrors/reflect_uninstantiated_class_test: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_classes_test/01: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_classes_test/02: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_classes_test/03: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_classes_test/none: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_function_type_test: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_special_types_test: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_test/01: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_test/02: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_test/03: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_test/none: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_typedefs_test: Crash # Internal Error: No default constructor available.
-mirrors/reflected_type_typevars_test: Crash # Internal Error: No default constructor available.
-mirrors/reflectively_instantiate_uninstantiated_class_test: Crash # Internal Error: No default constructor available.
-mirrors/regress_14304_test: Crash # Internal Error: No default constructor available.
-mirrors/regress_16321_test/01: Crash # Internal Error: No default constructor available.
-mirrors/regress_16321_test/none: Crash # Internal Error: No default constructor available.
-mirrors/regress_19731_test: Crash # Internal Error: No default constructor available.
-mirrors/relation_assignable_test: Crash # Internal Error: No default constructor available.
-mirrors/relation_subclass_test: Crash # Internal Error: No default constructor available.
-mirrors/relation_subtype_test: Crash # Internal Error: No default constructor available.
-mirrors/removed_api_test: Crash # Internal Error: No default constructor available.
-mirrors/repeated_private_anon_mixin_app_test: Crash # Internal Error: No default constructor available.
-mirrors/set_field_with_final_inheritance_test: Crash # Internal Error: No default constructor available.
-mirrors/set_field_with_final_test: Crash # Internal Error: No default constructor available.
-mirrors/spawn_function_root_library_test: Crash # Internal Error: No default constructor available.
-mirrors/static_members_easier_test: Crash # Internal Error: No default constructor available.
-mirrors/static_members_test: Crash # Internal Error: No default constructor available.
-mirrors/static_test: Crash # Internal Error: No default constructor available.
-mirrors/superclass2_test: Crash # Internal Error: No default constructor available.
-mirrors/superclass_test: Crash # Internal Error: No default constructor available.
-mirrors/symbol_validation_test/01: Crash # Internal Error: No default constructor available.
+convert/streamed_conversion_json_utf8_encode_test: RuntimeError # receiver.get$_collection$_nums is not a function
+convert/streamed_conversion_utf8_decode_test: RuntimeError # receiver.get$_nums is not a function
+convert/streamed_conversion_utf8_encode_test: RuntimeError # receiver.get$_nums is not a function
+math/pi_test: RuntimeError # receiver.get$_nums is not a function
+math/point_test: RuntimeError # receiver.get$_nums is not a function
+math/rectangle_test: RuntimeError # receiver.get$_nums is not a function
+mirrors/abstract_class_test/00: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/abstract_class_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/abstract_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/accessor_cache_overflow_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/array_tracing2_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/array_tracing_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/basic_types_in_dart_core_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/circular_factory_redirection_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/class_declarations_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/class_declarations_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/class_mirror_location_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/class_mirror_type_variables_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/closures_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/closurization_equivalence_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/constructor_kinds_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/constructor_kinds_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/constructors_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/dart2js_mirrors_test: Crash # (=Class.faktory;): Unhandled node
+mirrors/declarations_type_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/deferred_mirrors_metadata_test: RuntimeError # receiver.get$_collection$_nums is not a function
+mirrors/deferred_mirrors_metatarget_test: RuntimeError # receiver.get$_collection$_nums is not a function
+mirrors/deferred_mirrors_update_test: RuntimeError # receiver.get$_collection$_nums is not a function
+mirrors/deferred_type_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/delegate_call_through_getter_test: RuntimeError # Please triage this failure.
+mirrors/delegate_test: RuntimeError # Please triage this failure.
+mirrors/disable_tree_shaking_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/empty_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/enum_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/equality_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/fake_function_with_call_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/fake_function_without_call_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/field_type_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/function_type_mirror_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_bounded_by_type_parameter_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_bounded_by_type_parameter_test/02: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_bounded_by_type_parameter_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_bounded_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_bounded_test/02: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_bounded_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_class_declaration_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_f_bounded_mixin_application_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_f_bounded_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_f_bounded_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_function_typedef_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_interface_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_interface_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_list_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_local_function_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_mixin_applications_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_mixin_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_superclass_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_superclass_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generic_type_mirror_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generics_double_substitution_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generics_double_substitution_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generics_dynamic_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generics_special_types_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generics_substitution_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generics_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/generics_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/globalized_closures2_test/00: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/globalized_closures2_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/globalized_closures_test/00: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/globalized_closures_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/hierarchy_invariants_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/immutable_collections_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/inherit_field_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/initializing_formals_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/initializing_formals_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/instance_members_easier_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/instance_members_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/instance_members_unimplemented_interface_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/instance_members_with_override_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/instantiate_abstract_class_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/intercepted_cache_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/intercepted_class_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/intercepted_object_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/intercepted_superclass_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invocation_fuzz_test/emptyarray: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invocation_fuzz_test/false: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invocation_fuzz_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invocation_fuzz_test/smi: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invocation_fuzz_test/string: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_on_closure_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_through_getter_previously_accessed_test/named: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_through_getter_previously_accessed_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_through_getter_test/named: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_through_getter_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_through_implicit_getter_previously_accessed_test/named: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_through_implicit_getter_previously_accessed_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_call_through_implicit_getter_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_closurization2_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_closurization_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_import_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_named_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_named_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_natives_malicious_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/invoke_throws_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/is_odd_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/lazy_static_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/libraries_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_declarations_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_declarations_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_enumeration_deferred_loading_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_exports_hidden_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_exports_shown_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_import_deferred_loading_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_imports_bad_metadata_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_imports_deferred_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_imports_hidden_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_imports_metadata_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_imports_prefixed_show_hide_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_imports_prefixed_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_imports_shown_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_metadata2_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_metadata_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/library_uri_package_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/list_constructor_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/list_constructor_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/load_library_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/local_function_is_static_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/local_isolate_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_allowed_values_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_allowed_values_test/05: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_allowed_values_test/10: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_allowed_values_test/11: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_allowed_values_test/13: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_allowed_values_test/14: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_allowed_values_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_class_mirror_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_constructed_constant_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_constructor_arguments_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_nested_constructor_call_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/metadata_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/method_mirror_location_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/method_mirror_name_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/method_mirror_properties_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/method_mirror_returntype_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/method_mirror_source_line_ending_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/method_mirror_source_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mirror_in_static_init_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mirrors_nsm_mismatch_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mirrors_nsm_test/dart2js: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mirrors_nsm_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mirrors_reader_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mirrors_resolve_fields_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mirrors_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mixin_application_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mixin_members_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/mixin_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/native_class_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/new_instance_with_type_arguments_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/no_metadata_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/null2_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/null_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/operator_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/parameter_is_const_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/parameter_metadata_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/parameter_of_mixin_app_constructor_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/private_symbol_mangling_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/private_types_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/proxy_type_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/raw_type_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/raw_type_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/redirecting_factory_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/redirecting_factory_test/02: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/redirecting_factory_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflect_class_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflect_class_test/02: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflect_class_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflect_model_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflect_runtime_type_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflect_uninstantiated_class_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_classes_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_classes_test/02: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_classes_test/03: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_classes_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_function_type_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_special_types_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_test/02: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_test/03: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_typedefs_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflected_type_typevars_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/reflectively_instantiate_uninstantiated_class_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/regress_14304_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/regress_16321_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/regress_16321_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/regress_19731_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/relation_assignable_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/relation_subclass_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/relation_subtype_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/removed_api_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/repeated_private_anon_mixin_app_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/set_field_with_final_inheritance_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/set_field_with_final_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/spawn_function_root_library_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/static_members_easier_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/static_members_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/static_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/superclass2_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/superclass_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/symbol_validation_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
mirrors/symbol_validation_test/none: RuntimeError # Please triage this failure.
-mirrors/syntax_error_test/none: Crash # Internal Error: No default constructor available.
-mirrors/synthetic_accessor_properties_test: Crash # Internal Error: No default constructor available.
-mirrors/to_string_test: Crash # Internal Error: No default constructor available.
-mirrors/top_level_accessors_test: Crash # Internal Error: No default constructor available.
-mirrors/type_argument_is_type_variable_test: Crash # Internal Error: No default constructor available.
-mirrors/type_variable_is_static_test: Crash # Internal Error: No default constructor available.
-mirrors/type_variable_owner_test/01: Crash # Internal Error: No default constructor available.
-mirrors/type_variable_owner_test/none: Crash # Internal Error: No default constructor available.
-mirrors/typearguments_mirror_test: Crash # Internal Error: No default constructor available.
-mirrors/typedef_deferred_library_test : RuntimeError # TypeError: receiver.get$_collection$_nums is not a function
-mirrors/typedef_metadata_test: Crash # Internal Error: No default constructor available.
-mirrors/typedef_reflected_type_test/01: Crash # Internal Error: No default constructor available.
-mirrors/typedef_reflected_type_test/none: Crash # Internal Error: No default constructor available.
-mirrors/typedef_test: Crash # Internal Error: No default constructor available.
-mirrors/unnamed_library_test: Crash # Internal Error: No default constructor available.
-mirrors/variable_is_const_test/none: Crash # Internal Error: No default constructor available.
-typed_data/typed_data_list_test: RuntimeError # Please triage this failure.
+mirrors/syntax_error_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/synthetic_accessor_properties_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/to_string_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/top_level_accessors_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/type_argument_is_type_variable_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/type_variable_is_static_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/type_variable_owner_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/type_variable_owner_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/typearguments_mirror_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/typedef_deferred_library_test: RuntimeError # receiver.get$_collection$_nums is not a function
+mirrors/typedef_metadata_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/typedef_reflected_type_test/01: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/typedef_reflected_type_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/unnamed_library_test: Crash # (=_EmptyStream<T>;): Unhandled node
+mirrors/variable_is_const_test/none: Crash # (=_EmptyStream<T>;): Unhandled node
+typed_data/typed_data_list_test: RuntimeError # this.get$length is not a function
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 9edc6cc..3dd8e64 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -170,16 +170,15 @@
io/http_client_stays_alive_test: Skip # Timeout.
[ $compiler == dart2js && $cps_ir ]
-io/file_test: Crash # Invalid argument(s)
+io/file_test: Crash # (static testWriteFro... cannot handle async/sync*/async* functions
io/file_write_only_test: Crash # (main()async{asyncSt... cannot handle async/sync*/async* functions
io/http_bind_test: Crash # (testBindShared(Stri... cannot handle async/sync*/async* functions
-io/http_parser_test : RuntimeError # TypeError: receiver.get$_nums is not a function
+io/http_parser_test: RuntimeError # receiver.get$_nums is not a function
io/https_bad_certificate_test: Crash # (main()async{var cli... cannot handle async/sync*/async* functions
io/issue_22636_test: Crash # (test()async{server=... cannot handle async/sync*/async* functions
io/issue_22637_test: Crash # (test()async{server=... cannot handle async/sync*/async* functions
-io/observatory_test: Crash # Invalid argument(s)
+io/observatory_test: Crash # (=_ByteCallbackSink;): Unhandled node
io/socket_bind_test: Crash # (testListenCloseList... cannot handle async/sync*/async* functions
io/socket_source_address_test: Crash # (Future testConnect(... cannot handle async/sync*/async* functions
-priority_queue_stress_test: RuntimeError # receiver.get$_collection$_nums is not a function
-slowpath_safepoints_test: RuntimeError # Cannot read property 'prototype' of undefined
+priority_queue_stress_test: RuntimeError # receiver.get$_nums is not a function
typed_array_test: RuntimeError # receiver.get$_collection$_nums is not a function
diff --git a/tests/utils/utils.status b/tests/utils/utils.status
index 5bb2a3a..d690ff9 100644
--- a/tests/utils/utils.status
+++ b/tests/utils/utils.status
@@ -29,6 +29,6 @@
source_mirrors_test: Pass, RuntimeError # Issue 17662
[ $compiler == dart2js && $cps_ir ]
-dummy_compiler_test: Crash # Internal Error: No default constructor available.
-recursive_import_test: Crash # Internal Error: No default constructor available.
-source_mirrors_test: Crash # Internal Error: No default constructor available.
+dummy_compiler_test: Crash # The null object does not have a getter '_element'.
+recursive_import_test: Crash # The null object does not have a getter '_element'.
+source_mirrors_test: Crash # (Future<bool> run(Ur... cannot handle async/sync*/async* functions
diff --git a/tools/VERSION b/tools/VERSION
index e552928..0d220d1 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 1
MINOR 12
PATCH 0
-PRERELEASE 1
-PRERELEASE_PATCH 1
+PRERELEASE 2
+PRERELEASE_PATCH 0
diff --git a/tools/apps/update_homebrew/bin/update_homebrew.dart b/tools/apps/update_homebrew/bin/update_homebrew.dart
index a1e4586..d87631a 100644
--- a/tools/apps/update_homebrew/bin/update_homebrew.dart
+++ b/tools/apps/update_homebrew/bin/update_homebrew.dart
@@ -234,7 +234,7 @@
bin.install_symlink "#{libexec}/bin/dart"
bin.write_exec_script Dir["#{libexec}/bin/{pub,docgen,dart?*}"]
- if build.with? 'content-shell'
+ if build.with? 'dartium'
dartium_binary = 'Chromium.app/Contents/MacOS/Chromium'
prefix.install resource('dartium')
(bin+"dartium").write shim_script dartium_binary
@@ -260,7 +260,7 @@
--with-dartium:
To use with IntelliJ, set the Dartium execute home to:
- #{prefix}/Chromium.app
+ #{opt_prefix}/Chromium.app
EOS
end
diff --git a/tools/bots/bot.py b/tools/bots/bot.py
index 0015f47..ed20c5d 100644
--- a/tools/bots/bot.py
+++ b/tools/bots/bot.py
@@ -15,7 +15,11 @@
import subprocess
import sys
-DART_PATH = dirname(dirname(dirname(abspath(__file__))))
+import bot_utils
+
+utils = bot_utils.GetUtils()
+
+BUILD_OS = utils.GuessOS()
BUILDER_NAME = 'BUILDBOT_BUILDERNAME'
BUILDER_CLOBBER = 'BUILDBOT_CLOBBER'
@@ -151,7 +155,7 @@
build_info.PrintBuildInfo()
# Make sure we are in the dart directory
- os.chdir(DART_PATH)
+ os.chdir(bot_utils.DART_DIR)
try:
Clobber()
@@ -240,6 +244,32 @@
RunProcess(cmd)
+def RunTestRunner(build_info, path):
+ """
+ Runs the test package's runner on the package at 'path'.
+ """
+ sdk_bin = os.path.join(
+ bot_utils.DART_DIR,
+ utils.GetBuildSdkBin(BUILD_OS, build_info.mode, build_info.arch))
+
+ build_root = utils.GetBuildRoot(
+ BUILD_OS, build_info.mode, build_info.arch)
+ package_root = os.path.abspath(os.path.join(build_root, 'packages'))
+
+ dart_name = 'dart.exe' if build_info.system == 'windows' else 'dart'
+ dart_bin = os.path.join(sdk_bin, dart_name)
+
+ test_bin = os.path.abspath(
+ os.path.join('third_party', 'pkg', 'test', 'bin', 'test.dart'))
+
+ with utils.ChangedWorkingDirectory(path):
+ args = [dart_bin, '--package-root=' + package_root, test_bin,
+ '--package-root', package_root, '--reporter', 'expanded',
+ '--no-color']
+ print("Running %s" % ' '.join(args))
+ RunProcess(args)
+
+
def RunProcess(command):
"""
Runs command.
diff --git a/tools/bots/dart_services.py b/tools/bots/dart_services.py
deleted file mode 100644
index dd0fe3f..0000000
--- a/tools/bots/dart_services.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/python
-
-# Copyright (c) 2015, 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.
-
-"""
-Buildbot steps for stress testing analysis engine
-"""
-import os
-import shutil
-import sys
-import bot
-import bot_utils
-
-utils = bot_utils.GetUtils()
-
-def ServicesConfig(name, is_buildbot):
- """Returns info for the current buildbot.
- We only run this bot on linux, so all of this is just hard coded.
- """
- return bot.BuildInfo('none', 'none', 'release', 'linux')
-
-def Run(args):
- print "Running: %s" % ' '.join(args)
- sys.stdout.flush()
- bot.RunProcess(args)
-
-def ServicesSteps(build_info):
- build_root = utils.GetBuildRoot('linux')
- sdk_bin = utils.GetBuildSdkBin('linux', mode='release', arch='ia32')
- dart_services = os.path.join('third_party', 'dart-services')
- dart_services_copy = os.path.join(build_root, 'dart-services')
-
- with bot.BuildStep('Create copy of dart_services'):
- print 'Removing existing copy of dart_services'
- shutil.rmtree(dart_services_copy, ignore_errors=True)
- args = ['cp', '-R', dart_services, dart_services_copy]
- Run(args)
-
- with bot.BuildStep('Fixing pubspec file'):
- pubspec = os.path.join(dart_services_copy, 'pubspec.yaml')
- # TODO(lukechurch): Actually provide the name of the alternative pubspec
- testing_pubspec = os.path.join(dart_services_copy, 'pubspec.foobar.yaml')
- print 'Fixing pubspec up for stress testing'
- # TODO(lukechurch): change to do the mv of the testing pubspec
- Run(['ls', pubspec])
-
- with bot.BuildStep('Run pub'):
- print 'Print running pub'
- pub = os.path.join(sdk_bin, 'pub')
- with utils.ChangedWorkingDirectory(dart_services_copy):
- args = [pub, 'get']
-
- with bot.BuildStep('Stress testing'):
- # Consider doing something more useful here.
- args = ['ls', 'third_party']
- Run(args)
-
-
-if __name__ == '__main__':
- bot.RunBot(ServicesConfig, ServicesSteps)
-
diff --git a/tools/bots/pkg.py b/tools/bots/pkg.py
index 1ba85bb..b3e22a7 100644
--- a/tools/bots/pkg.py
+++ b/tools/bots/pkg.py
@@ -11,6 +11,7 @@
third_party/pkg_tested.
"""
+import os
import re
import sys
@@ -38,12 +39,6 @@
builder_tag=locale)
def PkgSteps(build_info):
- with bot.BuildStep('Build package-root'):
- args = [sys.executable, './tools/build.py', '--mode=' + build_info.mode,
- 'packages']
- print 'Building package-root: %s' % (' '.join(args))
- bot.RunProcess(args)
-
common_args = ['--write-test-outcome-log']
if build_info.builder_tag:
common_args.append('--builder-tag=%s' % build_info.builder_tag)
@@ -57,27 +52,31 @@
# Experiment with not running concurrent calls.
if build_info.system == 'windows':
common_args.append('-j1')
- if build_info.mode == 'release':
- bot.RunTest('pkg ', build_info,
- common_args + ['pkg', 'docs', 'pkg_tested'],
- swallow_error=True)
- else:
- # Pkg tests currently have a lot of timeouts when run in debug mode.
- # See issue 18479
- bot.RunTest('pkg', build_info, common_args + ['pkg', 'docs'],
- swallow_error=True)
- if build_info.mode == 'release':
- pkgbuild_build_info = bot.BuildInfo('none', 'vm', build_info.mode,
- build_info.system, checked=False)
- bot.RunTest('pkgbuild_repo_pkgs', pkgbuild_build_info,
- common_args + ['--append_logs', '--use-repository-packages',
- 'pkgbuild'],
- swallow_error=True)
+ bot.RunTest('pkg ', build_info,
+ common_args + ['pkg', 'docs'],
+ swallow_error=True)
- public_args = (common_args +
- ['--append_logs', '--use-public-packages', 'pkgbuild'])
- bot.RunTest('pkgbuild_public_pkgs', pkgbuild_build_info, public_args)
+ # Pkg tests currently have a lot of timeouts when run in debug mode.
+ # See issue 18479
+ if build_info.mode != 'release': return
+
+ with bot.BuildStep('third_party pkg tests', swallow_error=True):
+ pkg_tested = os.path.join('third_party', 'pkg_tested')
+ for entry in os.listdir(pkg_tested):
+ path = os.path.join(pkg_tested, entry)
+ if os.path.isdir(path): bot.RunTestRunner(build_info, path)
+
+ pkgbuild_build_info = bot.BuildInfo('none', 'vm', build_info.mode,
+ build_info.system, checked=False)
+ bot.RunTest('pkgbuild_repo_pkgs', pkgbuild_build_info,
+ common_args + ['--append_logs', '--use-repository-packages',
+ 'pkgbuild'],
+ swallow_error=True)
+
+ public_args = (common_args +
+ ['--append_logs', '--use-public-packages', 'pkgbuild'])
+ bot.RunTest('pkgbuild_public_pkgs', pkgbuild_build_info, public_args)
if __name__ == '__main__':
bot.RunBot(PkgConfig, PkgSteps)
diff --git a/tools/bots/pub.py b/tools/bots/pub.py
index ee3cd1e..cdd8a8b 100755
--- a/tools/bots/pub.py
+++ b/tools/bots/pub.py
@@ -12,15 +12,8 @@
import os
import re
-import shutil
-import sys
import bot
-import bot_utils
-
-utils = bot_utils.GetUtils()
-
-BUILD_OS = utils.GuessOS()
PUB_BUILDER = r'pub-(linux|mac|win)'
@@ -42,34 +35,11 @@
return bot.BuildInfo('none', 'vm', mode, system, checked=True)
-def Run(command):
- print "Running %s" % ' '.join(command)
- return bot.RunProcess(command)
-
def PubSteps(build_info):
- sdk_bin = os.path.join(
- bot_utils.DART_DIR,
- utils.GetBuildSdkBin(BUILD_OS, build_info.mode, build_info.arch))
- pub_script_name = 'pub.bat' if build_info.system == 'windows' else 'pub'
- pub_bin = os.path.join(sdk_bin, pub_script_name)
-
- pub_copy = os.path.join(utils.GetBuildRoot(BUILD_OS), 'pub_copy')
pub_location = os.path.join('third_party', 'pkg', 'pub')
- with bot.BuildStep('Make copy of pub for testing'):
- print 'Removing old copy %s' % pub_copy
- shutil.rmtree(pub_copy, ignore_errors=True)
- print 'Copying %s to %s' % (pub_location, pub_copy)
- shutil.copytree(pub_location, pub_copy)
-
- # TODO(nweiz): add logic for testing pub.
- with bot.BuildStep('Doing the magic ls'):
- with utils.ChangedWorkingDirectory(pub_copy):
- Run(['ls', '-l'])
-
- with bot.BuildStep('Running pub'):
- Run([pub_bin, '--version'])
-
+ with bot.BuildStep('Running pub tests'):
+ bot.RunTestRunner(build_info, pub_location)
if __name__ == '__main__':
bot.RunBot(PubConfig, PubSteps)
diff --git a/tools/clean_output_directory.py b/tools/clean_output_directory.py
index f94259b..e41671c 100755
--- a/tools/clean_output_directory.py
+++ b/tools/clean_output_directory.py
@@ -12,7 +12,13 @@
def Main():
build_root = utils.GetBuildRoot(utils.GuessOS())
print 'Deleting %s' % build_root
- shutil.rmtree(build_root, ignore_errors=True)
+ if sys.platform != 'win32':
+ shutil.rmtree(build_root, ignore_errors=True)
+ else:
+ # Intentionally ignore return value since a directory might be in use.
+ subprocess.call(['rmdir', '/Q', '/S', build_root],
+ env=os.environ.copy(),
+ shell=True)
return 0
if __name__ == '__main__':
diff --git a/tools/testing/dart/test_suite.dart b/tools/testing/dart/test_suite.dart
index f09f283..2304b5c 100644
--- a/tools/testing/dart/test_suite.dart
+++ b/tools/testing/dart/test_suite.dart
@@ -410,9 +410,9 @@
* Layout of packages inside the dart repository:
* dart/
* pkg/PACKAGE_NAME
- * pkg/third_party/PACKAGE_NAME
* third_party/pkg/PACKAGE_NAME
* runtime/observatory/PACKAGE_NAME
+ * sdk/lib/_internal/PACKAGE_NAME
*/
// Directories containing "-" are not valid pub packages and we therefore
@@ -425,6 +425,7 @@
listDir(dartDir.append('pkg'), isValid),
listDir(dartDir.append('third_party').append('pkg'), isValid),
listDir(dartDir.append('runtime').append('observatory'), isValid),
+ listDir(dartDir.append('sdk').append('lib').append('_internal'), isValid),
];
return Future.wait(futures).then((results) {
var packageDirectories = {};