Version 2.13.0-122.0.dev
Merge commit '927d45d0130ac307e9f0533e795f495aef287085' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index d1d72dc..bd38471 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -35,20 +35,21 @@
/// for any argument type. Lowers `getProperty(o, name)` to
/// `JS('Object|Null', '#.#', o, name)`.
StaticInvocation _lowerGetProperty(StaticInvocation node) {
- Arguments args = node.arguments;
- assert(args.positional.length == 2);
+ Arguments arguments = node.arguments;
+ assert(arguments.types.isEmpty);
+ assert(arguments.positional.length == 2);
+ assert(arguments.named.isEmpty);
return StaticInvocation(
_jsTarget,
Arguments(
[
StringLiteral("Object|Null"),
StringLiteral("#.#"),
- args.positional.first,
- args.positional.last
+ ...arguments.positional
],
// TODO(rileyporter): Copy type from getProperty when it's generic.
types: [DynamicType()],
- ))
+ )..fileOffset = node.arguments.fileOffset)
..fileOffset = node.fileOffset;
}
}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart b/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart
index 4475559..d478b33 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/sort_child_property_last.dart
@@ -6,6 +6,7 @@
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -45,15 +46,38 @@
}
await builder.addDartFileEdit(file, (fileEditBuilder) {
- var start = childProp.beginToken.previous.end;
- var end = childProp.endToken.next.end;
- var childRange = range.startOffsetEndOffset(start, end);
+ var hasTrailingComma = last.endToken.next.type == TokenType.COMMA;
+ var childStart = childProp.beginToken.previous.end;
+ var childEnd = childProp.endToken.next.end;
+ var childRange = range.startOffsetEndOffset(childStart, childEnd);
+
+ var deletionRange = childRange;
+ if (childProp == args.arguments.first) {
+ var deletionStart = childProp.offset;
+ var deletionEnd = args.arguments[1].offset;
+ deletionRange = range.startOffsetEndOffset(deletionStart, deletionEnd);
+ }
+
+ if (!hasTrailingComma) {
+ childEnd = childProp.end;
+ childRange = range.startOffsetEndOffset(childStart, childEnd);
+ }
var childText = utils.getRangeText(childRange);
- fileEditBuilder.addSimpleReplacement(childRange, '');
- fileEditBuilder.addSimpleInsertion(last.end + 1, childText);
- builder.setSelection(Position(file, last.end + 1));
+ var insertionPoint = last.end;
+ if (hasTrailingComma) {
+ insertionPoint = last.endToken.next.end;
+ } else if (childStart == childProp.offset) {
+ childText = ', $childText';
+ } else {
+ childText = ',$childText';
+ }
+
+ fileEditBuilder.addDeletion(deletionRange);
+ fileEditBuilder.addSimpleInsertion(insertionPoint, childText);
+
+ builder.setSelection(Position(file, insertionPoint));
});
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/sort_child_property_last_test.dart b/pkg/analysis_server/test/src/services/correction/assist/sort_child_property_last_test.dart
index 2aea7cc..b407482 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/sort_child_property_last_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/sort_child_property_last_test.dart
@@ -73,11 +73,12 @@
await assertNoAssist();
}
- Future<void> test_sort() async {
+ Future<void> test_sort_middleArgument() async {
await resolveTestCode('''
import 'package:flutter/material.dart';
main() {
Column(
+ mainAxisAlignment: MainAxisAlignment.start,
/*caret*/children: <Widget>[
Text('aaa'),
Text('bbbbbb'),
@@ -91,6 +92,7 @@
import 'package:flutter/material.dart';
main() {
Column(
+ mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text('aaa'),
@@ -121,4 +123,64 @@
''');
await assertNoAssist();
}
+
+ Future<void> test_sort_noTrailingComma() async {
+ await resolveTestCode('''
+import 'package:flutter/material.dart';
+main() {
+ Column(
+ /*caret*/children: <Widget>[
+ Text('aaa'),
+ Text('bbbbbb'),
+ Text('ccccccccc'),
+ ],
+ crossAxisAlignment: CrossAxisAlignment.center
+ );
+}
+''');
+ await assertHasAssist('''
+import 'package:flutter/material.dart';
+main() {
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: <Widget>[
+ Text('aaa'),
+ Text('bbbbbb'),
+ Text('ccccccccc'),
+ ]
+ );
+}
+''');
+ assertExitPosition(after: ']');
+ }
+
+ Future<void> test_sort_trailingComma() async {
+ await resolveTestCode('''
+import 'package:flutter/material.dart';
+main() {
+ Column(
+ /*caret*/children: <Widget>[
+ Text('aaa'),
+ Text('bbbbbb'),
+ Text('ccccccccc'),
+ ],
+ crossAxisAlignment: CrossAxisAlignment.center,
+ );
+}
+''');
+ await assertHasAssist('''
+import 'package:flutter/material.dart';
+main() {
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: <Widget>[
+ Text('aaa'),
+ Text('bbbbbb'),
+ Text('ccccccccc'),
+ ],
+ );
+}
+''');
+ assertExitPosition(after: '],');
+ }
}
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index 5223b4a..ec18a06 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -6,15 +6,16 @@
import 'dart:isolate';
import 'dart:typed_data';
-import 'package:analyzer/dart/analysis/context_locator.dart' as api;
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/context/packages.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/analysis/cache.dart';
+import 'package:analyzer/src/dart/analysis/context_root.dart' as api;
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'
as api;
@@ -32,11 +33,12 @@
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/summary2/package_bundle_format.dart';
import 'package:analyzer/src/summary2/reference.dart';
-import 'package:analyzer_cli/src/context_cache.dart';
+import 'package:analyzer/src/task/options.dart';
+import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer/src/workspace/bazel.dart';
import 'package:analyzer_cli/src/driver.dart';
import 'package:analyzer_cli/src/error_formatter.dart';
import 'package:analyzer_cli/src/error_severity.dart';
-import 'package:analyzer_cli/src/has_context_mixin.dart';
import 'package:analyzer_cli/src/options.dart';
import 'package:bazel_worker/bazel_worker.dart';
import 'package:collection/collection.dart';
@@ -81,7 +83,6 @@
var packageBundleProvider =
WorkerPackageBundleProvider(packageBundleCache, inputs);
var buildMode = BuildMode(resourceProvider, options, AnalysisStats(),
- ContextCache(resourceProvider, options, Driver.verbosePrint),
logger: logger, packageBundleProvider: packageBundleProvider);
await buildMode.analyze();
AnalysisEngine.instance.clearCaches();
@@ -154,17 +155,13 @@
}
/// Analyzer used when the "--build-mode" option is supplied.
-class BuildMode with HasContextMixin {
- @override
+class BuildMode {
final ResourceProvider resourceProvider;
final CommandLineOptions options;
final AnalysisStats stats;
final PerformanceLog logger;
final PackageBundleProvider packageBundleProvider;
- @override
- final ContextCache contextCache;
-
SummaryDataStore summaryDataStore;
AnalysisOptionsImpl analysisOptions;
Map<Uri, File> uriToFileMap;
@@ -179,7 +176,7 @@
// May be null.
final DependencyTracker dependencyTracker;
- BuildMode(this.resourceProvider, this.options, this.stats, this.contextCache,
+ BuildMode(this.resourceProvider, this.options, this.stats,
{PerformanceLog logger, PackageBundleProvider packageBundleProvider})
: logger = logger ?? PerformanceLog(null),
packageBundleProvider = packageBundleProvider ??
@@ -281,6 +278,16 @@
});
}
+ void _applyOptionsFile() {
+ analysisOptions = AnalysisOptionsImpl();
+
+ var path = options.contextBuilderOptions.defaultAnalysisOptionsFilePath;
+ var file = resourceProvider.getFile(path);
+ var provider = AnalysisOptionsProvider(sourceFactory);
+ var map = provider.getOptionsFromFile(file);
+ applyToAnalysisOptions(analysisOptions, map);
+ }
+
/// Use [elementFactory] filled with input summaries, and link libraries
/// in [explicitSources] to produce linked summary bytes.
Uint8List _computeLinkedLibraries2() {
@@ -394,8 +401,7 @@
}
});
- var rootPath =
- options.sourceFiles.isEmpty ? null : options.sourceFiles.first;
+ var rootPath = options.sourceFiles.first;
var packages = _findPackages(rootPath);
@@ -407,8 +413,7 @@
ExplicitSourceResolver(uriToFileMap)
]);
- analysisOptions =
- createAnalysisOptionsForCommandLineOptions(options, rootPath);
+ _applyOptionsFile();
var scheduler = AnalysisDriverScheduler(logger);
analysisDriver = AnalysisDriver(
@@ -524,25 +529,23 @@
}
void _setAnalysisDriverAnalysisContext(String rootPath) {
- if (rootPath == null) {
- return;
- }
-
- var apiContextRoots = api.ContextLocator(
- resourceProvider: resourceProvider,
- ).locateRoots(
- includedPaths: [rootPath],
- excludedPaths: [],
+ rootPath = file_paths.absoluteNormalized(
+ resourceProvider.pathContext,
+ rootPath,
);
- if (apiContextRoots.isEmpty) {
- return;
- }
+ var workspace = BazelWorkspace.find(resourceProvider, rootPath);
+
+ var contextRoot = api.ContextRootImpl(
+ resourceProvider,
+ resourceProvider.getFolder(workspace.root),
+ workspace,
+ );
analysisDriver.configure(
analysisContext: api.DriverBasedAnalysisContext(
resourceProvider,
- apiContextRoots.first,
+ contextRoot,
analysisDriver,
),
);
diff --git a/pkg/analyzer_cli/lib/src/context_cache.dart b/pkg/analyzer_cli/lib/src/context_cache.dart
deleted file mode 100644
index 876239c..0000000
--- a/pkg/analyzer_cli/lib/src/context_cache.dart
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2018, 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:analyzer/dart/analysis/features.dart';
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/context/builder.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
-import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer_cli/src/options.dart';
-import 'package:path/path.dart' as path;
-import 'package:pub_semver/pub_semver.dart';
-
-/// Cache of [AnalysisOptionsImpl] objects that correspond to directories
-/// with analyzed files, used to reduce searching for `analysis_options.yaml`
-/// files.
-class ContextCache {
- final ResourceProvider resourceProvider;
- final CommandLineOptions clOptions;
- final void Function(String) verbosePrint;
-
- /// A mapping from normalized paths (currently, directories) to cache entries
- /// which include builder, analysis_options paths, and [AnalysisOptionImpl]s.
- final Map<String, ContextCacheEntry> _byDirectory = {};
-
- ContextCache(this.resourceProvider, this.clOptions, this.verbosePrint);
-
- /// Look up info about a context from the cache. You can pass in any [path],
- /// and it will try to provide an existing [ContextCacheEntry] that is
- /// suitable for that [path] if one exists.
- ContextCacheEntry forSource(String path) {
- path = _normalizeSourcePath(path);
- return _byDirectory.putIfAbsent(path, () {
- final builder = ContextBuilder(resourceProvider, null, null,
- options: clOptions.contextBuilderOptions);
- return ContextCacheEntry(builder, path, clOptions, verbosePrint);
- });
- }
-
- /// Cheaply normalize source paths so we can increase cache performance.
- /// Getting the location of an analysis_options.yaml file for a given source
- /// can be expensive, so we want to reduce extra lookups where possible. We
- /// know that two files in the same directory share an analysis options file,
- /// so that's the normalization we perform currently. However, this could be
- /// any form of performance-increasing cache key normalization.
- String _normalizeSourcePath(String sourcePath) {
- if (!resourceProvider.pathContext.isAbsolute(sourcePath)) {
- // TODO(mfairhurst) Use resourceProvider.pathContext.absolute(). For the
- // moment, we get an issues where pathContext.current is `.`, which causes
- // pathContext.absolute() to produce `./foo` instead of `/absolute/foo`.
- sourcePath = path.absolute(sourcePath);
- }
-
- sourcePath = resourceProvider.pathContext.normalize(sourcePath);
-
- // Prepare the directory which is, or contains, the context root.
- if (resourceProvider.getFolder(sourcePath).exists) {
- return sourcePath;
- }
-
- return resourceProvider.pathContext.dirname(sourcePath);
- }
-}
-
-/// Each entry of the [ContextCache] caches three things: the [ContextBuilder],
-/// the analysis_options.yaml path of the context, and the [AnalysisOptionsImpl]
-/// of the context.
-class ContextCacheEntry {
- final CommandLineOptions clOptions;
- final ContextBuilder builder;
- final String requestedSourceDirectory;
- final void Function(String) verbosePrint;
-
- AnalysisOptionsImpl _analysisOptions;
- String _analysisRoot;
-
- ContextCacheEntry(this.builder, this.requestedSourceDirectory, this.clOptions,
- this.verbosePrint);
-
- /// Get the fully parsed [AnalysisOptionsImpl] for this entry.
- AnalysisOptionsImpl get analysisOptions =>
- _analysisOptions ??= _getAnalysisOptions();
-
- /// Find the root path from which excludes should be considered due to where
- /// the analysis_options.yaml was defined.
- String get analysisRoot => _analysisRoot ??= _getAnalysisRoot();
-
- void _buildContextFeatureSet(AnalysisOptionsImpl analysisOptions) {
- var featureSet = FeatureSet.fromEnableFlags2(
- sdkLanguageVersion: ExperimentStatus.currentVersion,
- flags: clOptions.enabledExperiments,
- );
-
- analysisOptions.contextFeatures = featureSet;
-
- if (clOptions.defaultLanguageVersion != null) {
- var nonPackageLanguageVersion = Version.parse(
- clOptions.defaultLanguageVersion + '.0',
- );
- analysisOptions.nonPackageLanguageVersion = nonPackageLanguageVersion;
- analysisOptions.nonPackageFeatureSet = FeatureSet.latestLanguageVersion()
- .restrictToVersion(nonPackageLanguageVersion);
- }
- }
-
- /// The actual calculation to get the [AnalysisOptionsImpl], with no caching.
- /// This should not be used except behind the getter which caches this result
- /// automatically.
- AnalysisOptionsImpl _getAnalysisOptions() {
- var workspace = ContextBuilder.createWorkspace(
- resourceProvider: builder.resourceProvider,
- options: builder.builderOptions,
- rootPath: requestedSourceDirectory,
- );
- var contextOptions = builder.getAnalysisOptions(
- requestedSourceDirectory, workspace,
- verbosePrint: clOptions.verbose ? verbosePrint : null);
-
- _buildContextFeatureSet(contextOptions);
- contextOptions.hint = !clOptions.disableHints;
- return contextOptions;
- }
-
- /// The actual calculation to get the analysis root, with no caching. This
- /// should not be used except behind the getter which caches this result
- /// automatically.
- String _getAnalysisRoot() {
- // The analysis yaml defines the root, if it exists.
- var analysisOptionsPath = builder
- .getOptionsFile(requestedSourceDirectory, forceSearch: true)
- ?.path;
-
- if (analysisOptionsPath == null) {
- return requestedSourceDirectory;
- }
-
- return path.dirname(analysisOptionsPath);
- }
-}
diff --git a/pkg/analyzer_cli/lib/src/driver.dart b/pkg/analyzer_cli/lib/src/driver.dart
index 2767510..f5d7076 100644
--- a/pkg/analyzer_cli/lib/src/driver.dart
+++ b/pkg/analyzer_cli/lib/src/driver.dart
@@ -35,10 +35,8 @@
import 'package:analyzer_cli/src/analyzer_impl.dart';
import 'package:analyzer_cli/src/batch_mode.dart';
import 'package:analyzer_cli/src/build_mode.dart';
-import 'package:analyzer_cli/src/context_cache.dart';
import 'package:analyzer_cli/src/error_formatter.dart';
import 'package:analyzer_cli/src/error_severity.dart';
-import 'package:analyzer_cli/src/has_context_mixin.dart';
import 'package:analyzer_cli/src/options.dart';
import 'package:analyzer_cli/src/perf_report.dart';
import 'package:analyzer_cli/starter.dart' show CommandLineStarter;
@@ -60,12 +58,9 @@
return linterNode is YamlMap && getValue(linterNode, 'rules') != null;
}
-class Driver with HasContextMixin implements CommandLineStarter {
+class Driver implements CommandLineStarter {
static final ByteStore analysisDriverMemoryByteStore = MemoryByteStore();
- @override
- ContextCache contextCache;
-
_AnalysisContextProvider _analysisContextProvider;
DriverBasedAnalysisContext analysisContext;
@@ -78,7 +73,6 @@
int _analyzedFileCount = 0;
/// The resource provider used to access the file system.
- @override
final ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE;
/// Collected analysis statistics.
@@ -400,9 +394,7 @@
await workerLoop.run();
return ErrorSeverity.NONE;
} else {
- return await BuildMode(resourceProvider, options, stats,
- ContextCache(resourceProvider, options, verbosePrint))
- .analyze();
+ return await BuildMode(resourceProvider, options, stats).analyze();
}
}
diff --git a/pkg/analyzer_cli/lib/src/has_context_mixin.dart b/pkg/analyzer_cli/lib/src/has_context_mixin.dart
deleted file mode 100644
index 554deca..0000000
--- a/pkg/analyzer_cli/lib/src/has_context_mixin.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2018, 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:analyzer/error/error.dart';
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer_cli/src/context_cache.dart';
-import 'package:analyzer_cli/src/options.dart';
-
-abstract class HasContextMixin {
- ContextCache get contextCache;
- ResourceProvider get resourceProvider;
-
- /// Based on the command line options, performantly get cached analysis
- /// options for the context.
- AnalysisOptionsImpl createAnalysisOptionsForCommandLineOptions(
- CommandLineOptions options, String source) {
- if (options.analysisOptionsFile != null) {
- var file = resourceProvider.getFile(options.analysisOptionsFile);
- if (!file.exists) {
- printAndFail('Options file not found: ${options.analysisOptionsFile}',
- exitCode: ErrorSeverity.ERROR.ordinal);
- }
- }
-
- return getContextInfo(options, source).analysisOptions;
- }
-
- /// Based on the [CommandLineOptions], and a specific [Source] within those
- /// options if any are specified, performantly get cached info about the
- /// context.
- ContextCacheEntry getContextInfo(CommandLineOptions options, String source) {
- if (options.sourceFiles.isEmpty) {
- return contextCache.forSource(resourceProvider.pathContext.current);
- }
-
- return contextCache.forSource(source);
- }
-}
diff --git a/pkg/analyzer_cli/test/driver_test.dart b/pkg/analyzer_cli/test/driver_test.dart
index 6d818d6..9cdec75 100644
--- a/pkg/analyzer_cli/test/driver_test.dart
+++ b/pkg/analyzer_cli/test/driver_test.dart
@@ -413,6 +413,7 @@
Future<void> _withTempDir(Future<void> Function() f) async {
await withTempDirAsync((tempDir) async {
this.tempDir = tempDir;
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
await f();
});
}
@@ -422,20 +423,27 @@
class BuildModeTest extends AbstractBuildModeTest {
Future<void> test_buildLinked() async {
await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
+ var inputUri = 'package:dart.my/a.dart';
+ var inputPath = path.join(tempDir, 'dart', 'my', 'lib', 'a.dart');
+ (File(inputPath)..parent.createSync(recursive: true))
+ .writeAsStringSync('');
+
var outputPath = path.join(tempDir, 'test_file.dart.sum');
- await _doDrive(path.join('data', 'test_file.dart'), additionalArgs: [
+ await _doDrive(inputPath, fileUri: inputUri, additionalArgs: [
'--build-summary-only',
'--build-summary-output=$outputPath'
]);
+
var output = File(outputPath);
expect(output.existsSync(), isTrue);
var bundle = PackageBundleReader(await output.readAsBytes());
- var testFileUri = 'file:///test_file.dart';
- expect(_linkedLibraryUriList(bundle), [testFileUri]);
+ expect(_linkedLibraryUriList(bundle), [inputUri]);
expect(
- _linkedLibraryUnitUriList(bundle, testFileUri),
- [testFileUri],
+ _linkedLibraryUnitUriList(bundle, inputUri),
+ [inputUri],
);
expect(exitCode, 0);
@@ -444,6 +452,7 @@
Future<void> test_buildLinked_invalidPartUri() async {
await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
var aDart = path.join(tempDir, 'a.dart');
var aUri = 'package:aaa/a.dart';
@@ -465,19 +474,35 @@
}
Future<void> test_buildSuppressExitCode_fail_whenFileNotFound() async {
- await _doDrive(path.join('data', 'non_existent_file.dart'),
- additionalArgs: ['--build-suppress-exit-code']);
- expect(exitCode, isNot(0));
+ await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
+ await _doDrive(path.join(tempDir, 'non_existent_file.dart'),
+ additionalArgs: ['--build-suppress-exit-code']);
+ expect(exitCode, isNot(0));
+ });
}
Future<void> test_buildSuppressExitCode_success_evenIfHasError() async {
- await _doDrive(path.join('data', 'file_with_error.dart'),
- additionalArgs: ['--build-suppress-exit-code']);
- expect(exitCode, 0);
+ await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
+ var inputUri = 'package:dart.my/a.dart';
+ var inputPath = path.join(tempDir, 'dart', 'my', 'lib', 'a.dart');
+ (File(inputPath)..parent.createSync(recursive: true))
+ .writeAsStringSync('error');
+
+ await _doDrive(inputPath, fileUri: inputUri, additionalArgs: [
+ '--build-suppress-exit-code',
+ ]);
+ expect(exitCode, 0);
+ });
}
Future<void> test_consumeLinked() async {
await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
var aDart = path.join(tempDir, 'a.dart');
var bDart = path.join(tempDir, 'b.dart');
var cDart = path.join(tempDir, 'c.dart');
@@ -542,6 +567,8 @@
Future<void> test_error_notUriPipePath() async {
await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
var testDart = path.join(tempDir, 'test.dart');
File(testDart).writeAsStringSync('var v = 42;');
@@ -556,10 +583,20 @@
}
Future<void> test_fail_whenHasError() async {
- await _doDrive(path.join('data', 'file_with_error.dart'));
- expect(exitCode, isNot(0));
+ await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
+ var inputUri = 'package:my/with_error.dart';
+ var inputPath = path.join(tempDir, 'my', 'lib', 'with_error.dart');
+ (File(inputPath)..parent.createSync(recursive: true))
+ .writeAsStringSync('error');
+
+ await _doDrive(inputPath, fileUri: inputUri);
+ expect(exitCode, isNot(0));
+ });
}
+ @FailingTest(reason: 'Why do we support this case?')
Future<void> test_noInputs() async {
await withTempDirAsync((tempDir) async {
var outputPath = path.join(tempDir, 'test.sum');
@@ -580,15 +617,27 @@
}
Future<void> test_noStatistics() async {
- await _doDrive(path.join('data', 'test_file.dart'));
- // Should not print statistics summary.
- expect(outSink.toString(), isEmpty);
- expect(errorSink.toString(), isEmpty);
- expect(exitCode, 0);
+ await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
+ var inputUri = 'package:my/a.dart';
+ var inputPath = path.join(tempDir, 'my', 'lib', 'a.dart');
+ (File(inputPath)..parent.createSync(recursive: true))
+ .writeAsStringSync('class A {}');
+
+ await _doDrive(inputPath, fileUri: inputUri);
+
+ // Should not print statistics summary.
+ expect(outSink.toString(), isEmpty);
+ expect(errorSink.toString(), isEmpty);
+ expect(exitCode, 0);
+ });
}
Future<void> test_onlyErrors_partFirst() async {
await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
var aDart = path.join(tempDir, 'a.dart');
var bDart = path.join(tempDir, 'b.dart');
@@ -616,6 +665,8 @@
Future<void> test_packageConfig_packagesOptions() async {
await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
var packagesPath = path.join(tempDir, 'aaa.packages');
var aaaRoot = path.join(tempDir, 'packages', 'aaa');
@@ -658,6 +709,8 @@
Future<void> test_packageConfig_relativeToFile() async {
await withTempDirAsync((tempDir) async {
+ File(path.join(tempDir, 'WORKSPACE')).writeAsStringSync('');
+
var packagesPath = path.join(tempDir, '.dart_tool/package_config.json');
var aaaRoot = path.join(tempDir, 'packages', 'aaa');
diff --git a/pkg/dev_compiler/lib/src/compiler/module_builder.dart b/pkg/dev_compiler/lib/src/compiler/module_builder.dart
index 847185d..04b15cc 100644
--- a/pkg/dev_compiler/lib/src/compiler/module_builder.dart
+++ b/pkg/dev_compiler/lib/src/compiler/module_builder.dart
@@ -96,6 +96,27 @@
}
}
+/// Transforms an ES6 [function] into a given module [format].
+///
+/// If the format is [ModuleFormat.es6] this will return [function] unchanged.
+///
+/// Because JS ASTs are immutable the resulting function will share as much
+/// structure as possible with the original. The transformation is a shallow one
+/// that affects the [ImportDeclaration]s from [items].
+///
+/// Returns a new function that combines all statements from tranformed imports
+/// from [items] and the body of the [function].
+Fun transformFunctionModuleFormat(
+ List<ModuleItem> items, Fun function, ModuleFormat format) {
+ switch (format) {
+ case ModuleFormat.amd:
+ return AmdModuleBuilder().buildFunctionWithImports(items, function);
+ default:
+ throw UnsupportedError(
+ 'Incremental build does not support $format module format');
+ }
+}
+
/// Base class for compiling ES6 modules into various ES5 module patterns.
///
/// This is a helper class for utilities and state that is shared by several
@@ -107,11 +128,16 @@
final statements = <Statement>[];
/// Collect [imports], [exports] and [statements] from the ES6 [module].
+ void visitProgram(Program module) {
+ visitModuleItems(module.body);
+ }
+
+ /// Collect [imports], [exports] and [statements] from the ES6 [items].
///
/// For exports, this will also add their body to [statements] in the
/// appropriate position.
- void visitProgram(Program module) {
- for (var item in module.body) {
+ void visitModuleItems(List<ModuleItem> items) {
+ for (var item in items) {
if (item is ImportDeclaration) {
visitImportDeclaration(item);
} else if (item is ExportDeclaration) {
@@ -137,6 +163,12 @@
void visitStatement(Statement node) {
statements.add(node);
}
+
+ void clear() {
+ imports.clear();
+ exports.clear();
+ statements.clear();
+ }
}
/// Generates modules for with our DDC `dart_library.js` loading mechanism.
@@ -260,31 +292,43 @@
class AmdModuleBuilder extends _ModuleBuilder {
AmdModuleBuilder();
- Program build(Program module) {
- var importStatements = <Statement>[];
+ /// Build a module variable definition for [import].
+ static Statement buildLoadModule(
+ Identifier moduleVar, ImportDeclaration import) =>
+ js.statement('const # = require(#);', [moduleVar, import.from]);
- // Collect imports/exports/statements.
- visitProgram(module);
+ /// Build library variable definitions for all libraries from [import].
+ static List<Statement> buildImports(
+ Identifier moduleVar, ImportDeclaration import) {
+ var items = <Statement>[];
- var dependencies = <LiteralString>[];
- var fnParams = <Parameter>[];
+ for (var importName in import.namedImports) {
+ // import * is not emitted by the compiler, so we don't handle it here.
+ assert(!importName.isStar);
+ var asName = importName.asName ?? importName.name;
+ items.add(js.statement(
+ 'const # = #.#', [asName, moduleVar, importName.name.name]));
+ }
+ return items;
+ }
+
+ /// Group libraries from [imports] by modules.
+ static Map<Identifier, ImportDeclaration> _collectModuleImports(
+ List<ImportDeclaration> imports) {
+ var result = <Identifier, ImportDeclaration>{};
for (var import in imports) {
// TODO(jmesserly): we could use destructuring once Atom supports it.
var moduleVar =
TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
- fnParams.add(moduleVar);
- dependencies.add(import.from);
- // TODO(jmesserly): optimize for the common case of a single import.
- for (var importName in import.namedImports) {
- // import * is not emitted by the compiler, so we don't handle it here.
- assert(!importName.isStar);
- var asName = importName.asName ?? importName.name;
- importStatements.add(js.statement(
- 'const # = #.#', [asName, moduleVar, importName.name.name]));
- }
+ result[moduleVar] = import;
}
- statements.insertAll(0, importStatements);
+ return result;
+ }
+
+ /// Build statements for [exports].
+ static List<Statement> buildExports(List<ExportDeclaration> exports) {
+ var items = <Statement>[];
if (exports.isNotEmpty) {
var exportedProps = <Property>[];
@@ -297,9 +341,59 @@
exportedProps.add(Property(js.string(alias.name), name.name));
}
}
- statements.add(js.comment('Exports:'));
- statements.add(Return(ObjectInitializer(exportedProps, multiline: true)));
+ items.add(js.comment('Exports:'));
+ items.add(Return(ObjectInitializer(exportedProps, multiline: true)));
}
+ return items;
+ }
+
+ /// Build function body with all necessary imports included.
+ ///
+ /// Used for the top level syntetic function generated during expression
+ /// compilation, in order to include all the context needed for evaluation
+ /// inside it.
+ ///
+ /// Returns a new function that combines all statements from tranformed
+ /// imports from [items] and the body of the [function].
+ Fun buildFunctionWithImports(List<ModuleItem> items, Fun function) {
+ clear();
+ visitModuleItems(items);
+
+ var moduleImports = _collectModuleImports(imports);
+ var importStatements = <Statement>[];
+
+ moduleImports.forEach((moduleVar, import) {
+ importStatements.add(buildLoadModule(moduleVar, import));
+ importStatements.addAll(buildImports(moduleVar, import));
+ });
+
+ return Fun(
+ function.params,
+ Block([...importStatements, ...statements, ...function.body.statements]),
+ );
+ }
+
+ Program build(Program module) {
+ // Collect imports/exports/statements.
+ visitProgram(module);
+
+ var moduleImports = _collectModuleImports(imports);
+ var importStatements = <Statement>[];
+
+ var fnParams = moduleImports.keys.toList();
+ var dependencies =
+ moduleImports.values.map((import) => import.from).toList();
+
+ moduleImports.forEach((moduleVar, import) {
+ importStatements.addAll(buildImports(moduleVar, import));
+ });
+
+ // Prepend import statetements.
+ statements.insertAll(0, importStatements);
+
+ // Append export statements.
+ statements.addAll(buildExports(exports));
+
var resultModule = NamedFunction(
loadFunctionIdentifier(module.name),
js.fun("function(#) { 'use strict'; #; }", [fnParams, statements]),
diff --git a/pkg/dev_compiler/lib/src/compiler/module_containers.dart b/pkg/dev_compiler/lib/src/compiler/module_containers.dart
index 2e03a63..0040d60 100644
--- a/pkg/dev_compiler/lib/src/compiler/module_containers.dart
+++ b/pkg/dev_compiler/lib/src/compiler/module_containers.dart
@@ -8,6 +8,9 @@
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
+/// Defines how to emit a value of a table
+typedef _emitValue<K> = js_ast.Expression Function(K, ModuleItemData);
+
/// Represents a top-level property hoisted to a top-level object.
class ModuleItemData {
/// The container that holds this module item in the emitted JS.
@@ -39,34 +42,39 @@
/// Name of the container in the emitted JS.
String name;
- /// Indicates if this table is being used in an incremental context (such as
- /// during expression evaluation).
- ///
- /// Set by `emitFunctionIncremental` in kernel/compiler.dart.
- bool incrementalMode = false;
-
/// Refers to the latest container if this container is sharded.
js_ast.Identifier containerId;
- /// Refers to the aggregated entrypoint into this container.
- ///
- /// Should only be accessed during expression evaluation since lookups are
- /// deoptimized in V8..
- js_ast.Identifier aggregatedContainerId;
-
final Map<K, ModuleItemData> moduleItems = {};
+ /// Incremental mode used for expression compilation
+ bool _incrementalMode = false;
+
+ /// Items accessed during incremental mode
+ final Set<K> incrementalModuleItems = {};
+
+ /// Indicates if this table is being used in an incremental context.
+ ///
+ /// Used during expression evaluation.
+ /// Set by `emitFunctionIncremental` in kernel/compiler.dart.
+ bool get incrementalMode => _incrementalMode;
+
+ /// Sets the container to incremental mode.
+ ///
+ /// Used during expression evaluating so only referenced items
+ /// will be emitted in a generated function.
+ ///
+ /// Note: the container cannot revert to non-incremental mode.
+ void setIncrementalMode() {
+ incrementalModuleItems.clear();
+ _incrementalMode = true;
+ }
+
/// Holds keys that will not be emitted when calling [emit].
final Set<K> _noEmit = {};
- /// Creates a container with a name, ID, and incremental ID used for
- /// expression evaluation.
- ///
- /// If [aggregatedId] is null, the container is not sharded, so the
- /// containerId is safe to use during eval.
- ModuleItemContainer._(
- this.name, this.containerId, js_ast.Identifier aggregatedId)
- : aggregatedContainerId = aggregatedId ?? containerId;
+ /// Creates a container with a name, ID
+ ModuleItemContainer._(this.name, this.containerId);
/// Creates an automatically sharding container backed by JS Objects.
factory ModuleItemContainer.asObject(String name,
@@ -107,15 +115,17 @@
_noEmit.add(key);
}
+ void setEmitIfIncremental(K key) {
+ if (incrementalMode) {
+ incrementalModuleItems.add(key);
+ }
+ }
+
/// Emit the container declaration/initializer, using multiple statements if
/// necessary.
- List<js_ast.Statement> emit();
-
- /// Emit the container declaration/initializer incrementally.
///
- /// Used during expression evaluation. Appends all newly added types to the
- /// aggregated container.
- List<js_ast.Statement> emitIncremental();
+ /// Uses [emitValue] to emit the values in the table.
+ List<js_ast.Statement> emit({_emitValue<K> emitValue});
}
/// Associates a [K] with a container-unique JS key and arbitrary JS value.
@@ -142,8 +152,7 @@
String Function(K) keyToString;
ModuleItemObjectContainer(String name, this.keyToString)
- : super._(
- name, js_ast.TemporaryId(name), js_ast.Identifier('${name}\$Eval'));
+ : super._(name, js_ast.TemporaryId(name));
@override
void operator []=(K key, js_ast.Expression value) {
@@ -170,53 +179,33 @@
@override
js_ast.Expression access(K key) {
- var id = incrementalMode ? aggregatedContainerId : moduleItems[key].id;
+ var id = moduleItems[key].id;
return js.call('#.#', [id, moduleItems[key].jsKey]);
}
@override
- List<js_ast.Statement> emit() {
+ List<js_ast.Statement> emit({_emitValue<K> emitValue}) {
var containersToProperties = <js_ast.Identifier, List<js_ast.Property>>{};
moduleItems.forEach((k, v) {
- if (_noEmit.contains(k)) return;
+ if (!incrementalMode && _noEmit.contains(k)) return;
+ if (incrementalMode && !incrementalModuleItems.contains(k)) return;
+
if (!containersToProperties.containsKey(v.id)) {
containersToProperties[v.id] = <js_ast.Property>[];
}
- containersToProperties[v.id].add(js_ast.Property(v.jsKey, v.jsValue));
+ containersToProperties[v.id].add(js_ast.Property(
+ v.jsKey, emitValue == null ? v.jsValue : emitValue(k, v)));
});
- // Emit a self-reference for the next container so V8 does not optimize it
- // away. Required for expression evaluation.
- if (containersToProperties[containerId] == null) {
- containersToProperties[containerId] = [
- js_ast.Property(
- js_ast.LiteralString('_'), js.call('() => #', [containerId]))
- ];
- }
+ if (containersToProperties.isEmpty) return [];
+
var statements = <js_ast.Statement>[];
- var aggregatedContainers = <js_ast.Expression>[];
containersToProperties.forEach((containerId, properties) {
var containerObject = js_ast.ObjectInitializer(properties,
multiline: properties.length > 1);
statements.add(js.statement('var # = #', [containerId, containerObject]));
- aggregatedContainers.add(js.call('#', [containerId]));
});
- // Create an aggregated access point over all containers for eval.
- statements.add(js.statement('var # = Object.assign({_ : () => #}, #)',
- [aggregatedContainerId, aggregatedContainerId, aggregatedContainers]));
- return statements;
- }
- /// Appends all newly added types to the most recent container.
- @override
- List<js_ast.Statement> emitIncremental() {
- assert(incrementalMode);
- var statements = <js_ast.Statement>[];
- moduleItems.forEach((k, v) {
- if (_noEmit.contains(k)) return;
- statements.add(js
- .statement('#[#] = #', [aggregatedContainerId, v.jsKey, v.jsValue]));
- });
return statements;
}
}
@@ -232,7 +221,7 @@
/// ```
class ModuleItemArrayContainer<K> extends ModuleItemContainer<K> {
ModuleItemArrayContainer(String name)
- : super._(name, js_ast.TemporaryId(name), null);
+ : super._(name, js_ast.TemporaryId(name));
@override
void operator []=(K key, js_ast.Expression value) {
@@ -246,23 +235,26 @@
@override
js_ast.Expression access(K key) {
- var id = incrementalMode ? aggregatedContainerId : containerId;
+ var id = containerId;
return js.call('#[#]', [id, moduleItems[key].jsKey]);
}
@override
- List<js_ast.Statement> emit() {
+ List<js_ast.Statement> emit({_emitValue<K> emitValue}) {
var properties = List<js_ast.Expression>.filled(length, null);
// If the entire array holds just one value, generate a short initializer.
var valueSet = <js_ast.Expression>{};
moduleItems.forEach((k, v) {
- if (_noEmit.contains(k)) return;
+ if (!incrementalMode && _noEmit.contains(k)) return;
+ if (incrementalMode && !incrementalModuleItems.contains(k)) return;
valueSet.add(v.jsValue);
properties[int.parse((v.jsKey as js_ast.LiteralNumber).value)] =
- v.jsValue;
+ emitValue == null ? v.jsValue : emitValue(k, v);
});
+ if (valueSet.isEmpty) return [];
+
if (valueSet.length == 1 && moduleItems.length > 1) {
return [
js.statement('var # = Array(#).fill(#)', [
@@ -281,16 +273,4 @@
])
];
}
-
- @override
- List<js_ast.Statement> emitIncremental() {
- assert(incrementalMode);
- var statements = <js_ast.Statement>[];
- moduleItems.forEach((k, v) {
- if (_noEmit.contains(k)) return;
- statements.add(js
- .statement('#[#] = #', [aggregatedContainerId, v.jsKey, v.jsValue]));
- });
- return statements;
- }
}
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
index 363ff0b..d83b346 100644
--- a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
+++ b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
@@ -30,6 +30,9 @@
final _symbolContainer = ModuleItemContainer<js_ast.Identifier>.asObject('S',
keyToString: (js_ast.Identifier i) => '${i.name}');
+ ModuleItemContainer<js_ast.Identifier> get symbolContainer =>
+ _symbolContainer;
+
/// Extension member symbols for adding Dart members to JS types.
///
/// These are added to the [extensionSymbolsModule]; see that field for more
@@ -43,6 +46,47 @@
/// Imported libraries, and the temporaries used to refer to them.
final _imports = <Library, js_ast.TemporaryId>{};
+ /// Incremental mode for expression compilation.
+ ///
+ /// If set to true, triggers emitting tall used ypes, symbols, libraries,
+ /// constants, urs inside the generated function.
+ bool _incrementalMode = false;
+
+ @protected
+ bool get incrementalMode => _incrementalMode;
+
+ /// Set incremental mode for one expression compilation.
+ ///
+ /// Sets all tables and internal structures to incremental mode so
+ /// only referenced items will be emitted in a generated function.
+ ///
+ /// Note: the compiler cannot revert to non-incremental mode.
+ @protected
+ void setIncrementalMode() {
+ incrementalModules.clear();
+ _privateNames.clear();
+ symbolContainer.setIncrementalMode();
+ _incrementalMode = true;
+ }
+
+ /// Modules and libraries accessed during compilation in incremental mode.
+ @protected
+ final Map<String, Set<String>> incrementalModules = {};
+
+ @protected
+ void setEmitIfIncrementalLibrary(Library library) {
+ if (incrementalMode && library != null) {
+ setEmitIfIncremental(libraryToModule(library), jsLibraryName(library));
+ }
+ }
+
+ @protected
+ void setEmitIfIncremental(String module, String library) {
+ if (incrementalMode && library != null) {
+ incrementalModules.putIfAbsent(module, () => {}).add(library);
+ }
+ }
+
/// The identifier used to reference DDC's core "dart:_runtime" library from
/// generated JS code, typically called "dart" e.g. `dart.dcall`.
js_ast.Identifier runtimeModule;
@@ -220,8 +264,10 @@
/// dart.asInt(<expr>)
///
@protected
- js_ast.Expression runtimeCall(String code, [List<Object> args]) =>
- js.call('#.$code', <Object>[runtimeModule, ...?args]);
+ js_ast.Expression runtimeCall(String code, [List<Object> args]) {
+ setEmitIfIncremental(libraryToModule(coreLibrary), runtimeModule.name);
+ return js.call('#.$code', <Object>[runtimeModule, ...?args]);
+ }
/// Calls [runtimeCall] and uses `toStatement()` to convert the resulting
/// expression into a statement.
@@ -276,7 +322,13 @@
}
var privateNames = _privateNames.putIfAbsent(library, () => HashMap());
- return privateNames.putIfAbsent(name, initPrivateNameSymbol);
+ var symbolId = privateNames.putIfAbsent(name, initPrivateNameSymbol);
+
+ setEmitIfIncrementalLibrary(library);
+ setEmitIfIncremental(libraryToModule(coreLibrary), runtimeModule.name);
+ _symbolContainer.setEmitIfIncremental(symbolId);
+
+ return symbolId;
}
/// Emits a private name JS Symbol for [memberName] unique to a Dart
@@ -445,7 +497,6 @@
.statement('var # = Object.create(#.library)', [id, runtimeModule]));
exports.add(js_ast.NameSpecifier(id));
}
-
items.add(js_ast.ExportDeclaration(js_ast.ExportClause(exports)));
if (isBuildingSdk) {
@@ -469,6 +520,8 @@
/// Returns the canonical name to refer to the Dart library.
js_ast.Identifier emitLibraryName(Library library) {
+ setEmitIfIncrementalLibrary(library);
+
// Avoid adding the dart:_runtime to _imports when our runtime unit tests
// import it explicitly. It will always be implicitly imported.
if (isSdkInternalRuntime(library)) return runtimeModule;
@@ -479,12 +532,10 @@
library, () => js_ast.TemporaryId(jsLibraryName(library)));
}
- /// Emits imports and extension methods into [items].
+ /// Emits imports into [items].
@protected
- void emitImportsAndExtensionSymbols(List<js_ast.ModuleItem> items,
- {bool forceExtensionSymbols = false}) {
+ void emitImports(List<js_ast.ModuleItem> items) {
var modules = <String, List<Library>>{};
-
for (var import in _imports.keys) {
modules.putIfAbsent(libraryToModule(import), () => []).add(import);
}
@@ -493,33 +544,57 @@
if (!_libraries.containsKey(coreLibrary)) {
coreModuleName = libraryToModule(coreLibrary);
}
+
modules.forEach((module, libraries) {
- // Generate import directives.
- //
- // Our import variables are temps and can get renamed. Since our renaming
- // is integrated into js_ast, it is aware of this possibility and will
- // generate an "as" if needed. For example:
- //
- // import {foo} from 'foo'; // if no rename needed
- // import {foo as foo$} from 'foo'; // if rename was needed
- //
- var imports = libraries.map((library) {
- var alias = jsLibraryAlias(library);
- if (alias != null) {
- var aliasId = js_ast.TemporaryId(alias);
- return js_ast.NameSpecifier(aliasId, asName: _imports[library]);
+ if (!incrementalMode || incrementalModules.containsKey(module)) {
+ var usedLibraries = incrementalModules[module];
+
+ // Generate import directives.
+ //
+ // Our import variables are temps and can get renamed. Since our renaming
+ // is integrated into js_ast, it is aware of this possibility and will
+ // generate an "as" if needed. For example:
+ //
+ // import {foo} from 'foo'; // if no rename needed
+ // import {foo as foo$} from 'foo'; // if rename was needed
+ //
+ var imports = <js_ast.NameSpecifier>[];
+ for (var library in libraries) {
+ if (!incrementalMode ||
+ usedLibraries.contains(jsLibraryName(library))) {
+ var alias = jsLibraryAlias(library);
+ if (alias != null) {
+ var aliasId = js_ast.TemporaryId(alias);
+ imports.add(
+ js_ast.NameSpecifier(aliasId, asName: _imports[library]));
+ } else {
+ imports.add(js_ast.NameSpecifier(_imports[library]));
+ }
+ }
}
- return js_ast.NameSpecifier(_imports[library]);
- }).toList();
- if (module == coreModuleName) {
- imports.add(js_ast.NameSpecifier(runtimeModule));
- imports.add(js_ast.NameSpecifier(extensionSymbolsModule));
+
+ if (module == coreModuleName) {
+ if (!incrementalMode || usedLibraries.contains(runtimeModule.name)) {
+ imports.add(js_ast.NameSpecifier(runtimeModule));
+ }
+ if (!incrementalMode ||
+ usedLibraries.contains(extensionSymbolsModule.name)) {
+ imports.add(js_ast.NameSpecifier(extensionSymbolsModule));
+ }
+ }
+
+ if (!incrementalMode || imports.isNotEmpty) {
+ items.add(js_ast.ImportDeclaration(
+ namedImports: imports, from: js.string(module, "'")));
+ }
}
-
- items.add(js_ast.ImportDeclaration(
- namedImports: imports, from: js.string(module, "'")));
});
+ }
+ /// Emits extension methods into [items].
+ @protected
+ void emitExtensionSymbols(List<js_ast.ModuleItem> items,
+ {bool forceExtensionSymbols = false}) {
// Initialize extension symbols
_extensionSymbols.forEach((name, id) {
js_ast.Expression value =
@@ -530,16 +605,84 @@
value = js.call(
'# || (# = Symbol(#))', [value, value, js.string('dartx.$name')]);
}
- if (!_symbolContainer.canEmit(id)) {
- // Extension symbols marked with noEmit are managed manually.
- // TODO(vsm): Change back to `const`.
- // See https://github.com/dart-lang/sdk/issues/40380.
- items.add(js.statement('var # = #;', [id, value]));
+ // Emit hoisted extension symbols that are marked as noEmit in regular as
+ // well as incremental mode (if needed) since they are going to be
+ // referenced as such in the generated expression.
+ if (!incrementalMode ||
+ _symbolContainer.incrementalModuleItems.contains(id)) {
+ if (!_symbolContainer.canEmit(id)) {
+ // Extension symbols marked with noEmit are managed manually.
+ // TODO(vsm): Change back to `const`.
+ // See https://github.com/dart-lang/sdk/issues/40380.
+ items.add(js.statement('var # = #;', [id, value]));
+ }
+ }
+ if (_symbolContainer.incrementalModuleItems.contains(id)) {
+ setEmitIfIncremental(
+ libraryToModule(coreLibrary), extensionSymbolsModule.name);
}
_symbolContainer[id] = value;
});
}
+ /// Emits exports as imports into [items].
+ ///
+ /// Use information from exports to re-define library variables referenced
+ /// inside compiled expressions in incremental mode. That matches importing
+ /// a current module into the symbol used to represent the library during
+ /// original compilation in [ProgramCompiler.emitModule].
+ ///
+ /// Example of exports emitted to JavaScript during emitModule:
+ ///
+ /// ```
+ /// dart.trackLibraries("web/main", { ... });
+ /// // Exports:
+ /// return {
+ /// web__main: main
+ /// };
+ /// ```
+ ///
+ /// The transformation to imports during expression compilation converts the
+ /// exports above to:
+ ///
+ /// ```
+ /// const web__main = require('web/main');
+ /// const main = web__main.web__main;
+ /// ```
+ ///
+ /// Where the compiled expression references `main`.
+ @protected
+ void emitExportsAsImports(List<js_ast.ModuleItem> items, Library current) {
+ var exports = <js_ast.NameSpecifier>[];
+ assert(incrementalMode);
+ assert(!isBuildingSdk);
+
+ var module = libraryToModule(current);
+ var usedLibraries = incrementalModules[module] ?? {};
+
+ if (usedLibraries.isNotEmpty) {
+ _libraries.forEach((library, libraryId) {
+ if (usedLibraries.contains(jsLibraryName(library))) {
+ var alias = jsLibraryAlias(library);
+ var aliasId = alias == null ? libraryId : js_ast.TemporaryId(alias);
+ var asName = alias == null ? null : libraryId;
+ exports.add(js_ast.NameSpecifier(aliasId, asName: asName));
+ }
+ });
+
+ items.add(js_ast.ImportDeclaration(
+ namedImports: exports, from: js.string(module, "'")));
+ }
+ }
+
+ /// Emits imports and extension methods into [items].
+ @protected
+ void emitImportsAndExtensionSymbols(List<js_ast.ModuleItem> items,
+ {bool forceExtensionSymbols = false}) {
+ emitImports(items);
+ emitExtensionSymbols(items, forceExtensionSymbols: forceExtensionSymbols);
+ }
+
void _emitDebuggerExtensionInfo(String name) {
var properties = <js_ast.Property>[];
var parts = <js_ast.Property>[];
@@ -568,27 +711,26 @@
///
/// A symbol lookup on an id marked no emit omits the symbol accessor.
js_ast.Expression getSymbol(js_ast.Identifier id) {
+ _symbolContainer.setEmitIfIncremental(id);
return _symbolContainer.canEmit(id) ? _symbolContainer.access(id) : id;
}
/// Returns the raw JS value associated with [id].
js_ast.Expression getSymbolValue(js_ast.Identifier id) {
+ _symbolContainer.setEmitIfIncremental(id);
return _symbolContainer[id];
}
/// Inserts a symbol into the symbol table.
js_ast.Expression addSymbol(js_ast.Identifier id, js_ast.Expression symbol) {
_symbolContainer[id] = symbol;
+ _symbolContainer.setEmitIfIncremental(id);
if (!containerizeSymbols) {
_symbolContainer.setNoEmit(id);
}
return _symbolContainer[id];
}
- void setSymbolContainerIncrementalMode(bool setting) {
- _symbolContainer.incrementalMode = setting;
- }
-
/// Finishes the module created by [startModule], by combining the preable
/// [items] with the [moduleItems] that have been emitted.
///
@@ -646,7 +788,9 @@
_extensionSymbols[name] = id;
addSymbol(id, id);
}
- return _extensionSymbols[name];
+ var symbolId = _extensionSymbols[name];
+ _symbolContainer.setEmitIfIncremental(symbolId);
+ return symbolId;
}
/// Shorthand for identifier-like property names.
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index faae4a6..a1437e9 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -26,7 +26,7 @@
import '../compiler/shared_command.dart' show SharedCompilerOptions;
import '../compiler/shared_compiler.dart';
import '../js_ast/js_ast.dart' as js_ast;
-import '../js_ast/js_ast.dart' show js;
+import '../js_ast/js_ast.dart' show ModuleItem, js;
import '../js_ast/source_map_printer.dart' show NodeEnd, NodeSpan, HoverComment;
import 'constants.dart';
import 'js_interop.dart';
@@ -77,7 +77,7 @@
final _constLazyAccessors = <js_ast.Method>[];
/// Container for holding the results of lazily-evaluated constants.
- final _constTableCache = ModuleItemContainer<String>.asArray('C');
+ var _constTableCache = ModuleItemContainer<String>.asArray('C');
/// Tracks the index in [moduleItems] where the const table must be inserted.
/// Required for SDK builds due to internal circular dependencies.
@@ -221,7 +221,7 @@
final constAliasCache = HashMap<Constant, js_ast.Expression>();
/// Maps uri strings in asserts and elsewhere to hoisted identifiers.
- final _uriContainer = ModuleItemContainer<String>.asArray('I');
+ var _uriContainer = ModuleItemContainer<String>.asArray('I');
final Class _jsArrayClass;
final Class _privateSymbolClass;
@@ -242,6 +242,8 @@
final NullableInference _nullableInference;
+ bool _moduleEmitted = false;
+
factory ProgramCompiler(
Component component,
ClassHierarchy hierarchy,
@@ -320,8 +322,10 @@
InterfaceType get internalSymbolType =>
_coreTypes.legacyRawType(_coreTypes.internalSymbolClass);
+ /// Module can be emitted only once, and the compiler can be reused after
+ /// only in incremental mode, for expression compilation only.
js_ast.Program emitModule(Component component) {
- if (moduleItems.isNotEmpty) {
+ if (_moduleEmitted) {
throw StateError('Can only call emitModule once.');
}
_component = component;
@@ -443,7 +447,12 @@
// Emit the hoisted type table cache variables
items.addAll(_typeTable.dischargeBoundTypes());
- return finishModule(items, _options.moduleName);
+ var module = finishModule(items, _options.moduleName);
+
+ // Mark as finished for incremental mode, so it is safe to
+ // switch to the incremental mode for expression compilation.
+ _moduleEmitted = true;
+ return module;
}
@override
@@ -2530,8 +2539,14 @@
return libraryJSName != null ? '$libraryJSName.$jsName' : jsName;
}
+ String _emitJsNameWithoutGlobal(NamedNode n) {
+ if (!usesJSInterop(n)) return null;
+ setEmitIfIncrementalLibrary(getLibrary(n));
+ return _jsNameWithoutGlobal(n);
+ }
+
js_ast.PropertyAccess _emitJSInterop(NamedNode n) {
- var jsName = _jsNameWithoutGlobal(n);
+ var jsName = _emitJsNameWithoutGlobal(n);
if (jsName == null) return null;
return _emitJSInteropForGlobal(jsName);
}
@@ -2762,7 +2777,7 @@
typeRep = runtimeCall(
'anonymousJSType(#)', [js.escapedString(getLocalClassName(c))]);
} else {
- var jsName = _jsNameWithoutGlobal(c);
+ var jsName = _emitJsNameWithoutGlobal(c);
if (jsName != null) {
typeRep = runtimeCall('lazyJSType(() => #, #)',
[_emitJSInteropForGlobal(jsName), js.escapedString(jsName)]);
@@ -3072,6 +3087,30 @@
js_ast.Expression visitTypedefType(TypedefType type) =>
visitFunctionType(type.unalias as FunctionType);
+ /// Set incremental mode for expression compilation.
+ ///
+ /// Called for each expression compilation to set the intremental mode
+ /// and clear referenced items.
+ ///
+ /// The compiler cannot revert to non-incremental mode, and requires the
+ /// original module to be already emitted by the same compiler instance.
+ @override
+ void setIncrementalMode() {
+ if (!_moduleEmitted) {
+ throw StateError(
+ 'Cannot run in incremental mode before module completion');
+ }
+ super.setIncrementalMode();
+
+ _constTableCache = ModuleItemContainer<String>.asArray('C');
+ _constLazyAccessors.clear();
+ constAliasCache.clear();
+
+ _uriContainer = ModuleItemContainer<String>.asArray('I');
+
+ _typeTable.typeContainer.setIncrementalMode();
+ }
+
/// Emits function after initial compilation.
///
/// Emits function from kernel [functionNode] with name [name] in the context
@@ -3079,68 +3118,67 @@
/// finished. For example, this happens in expression compilation during
/// expression evaluation initiated by the user from the IDE and coordinated
/// by the debugger.
- js_ast.Fun emitFunctionIncremental(
- Library library, Class cls, FunctionNode functionNode, String name) {
- // setup context
+ /// Triggers incremental mode, which only emits symbols, types, constants,
+ /// libraries, and uris referenced in the expression compilation result.
+ js_ast.Fun emitFunctionIncremental(List<ModuleItem> items, Library library,
+ Class cls, FunctionNode functionNode, String name) {
+ // Setup context.
_currentLibrary = library;
_staticTypeContext.enterLibrary(_currentLibrary);
_currentClass = cls;
+ // Keep all symbols in containers.
+ containerizeSymbols = true;
+
+ // Set all tables to incremental mode, so we can only emit elements that
+ // were referenced the compiled code for the expression.
+ setIncrementalMode();
+
// Do not add formal parameter checks for the top-level synthetic function
// generated for expression evaluation, as those parameters are a set of
- // variables from the current scope, and should alredy be checked in the
+ // variables from the current scope, and should already be checked in the
// original code.
_checkParameters = false;
- // Set module item containers to incremental mode.
- setSymbolContainerIncrementalMode(true);
- _typeTable.typeContainer.incrementalMode = true;
- _constTableCache.incrementalMode = true;
-
- // Emit function with additional information, such as types that are used
- // in the expression.
+ // Emit function while recoding elements accessed from tables.
var fun = _emitFunction(functionNode, name);
- var types = _typeTable.dischargeBoundTypes();
- var constants = _dischargeConstTable();
+ var extensionSymbols = <js_ast.Statement>[];
+ emitExtensionSymbols(extensionSymbols);
- var body = js_ast.Block([...?types, ...?constants, ...fun.body.statements]);
+ // Add all elements from tables accessed in the function
+ var body = js_ast.Block([
+ ...extensionSymbols,
+ ..._typeTable.dischargeBoundTypes(),
+ ...symbolContainer.emit(),
+ ..._emitConstTable(),
+ ..._uriContainer.emit(),
+ ...fun.body.statements
+ ]);
+
+ // Import all necessary libraries, including libraries accessed from the
+ // current module and libraries accessed from the type table.
+ for (var library in _typeTable.incrementalLibraries()) {
+ setEmitIfIncrementalLibrary(library);
+ }
+ emitImports(items);
+ emitExportsAsImports(items, _currentLibrary);
+
return js_ast.Fun(fun.params, body);
}
- /// Emit all collected const symbols
- ///
- /// This is similar to how constants are emitted during
- /// initial compilation in emitModule
- ///
- /// TODO: unify the code with emitModule.
- List<js_ast.Statement> _dischargeConstTable() {
- var items = <js_ast.Statement>[];
-
+ List<js_ast.Statement> _emitConstTable() {
+ var constTable = <js_ast.Statement>[];
if (_constLazyAccessors.isNotEmpty) {
- var constTableBody = runtimeStatement(
- 'defineLazy(#, { # }, false)', [_constTable, _constLazyAccessors]);
- items.add(constTableBody);
- _constLazyAccessors.clear();
- }
+ constTable
+ .add(js.statement('const # = Object.create(null);', [_constTable]));
- _copyAndFlattenBlocks(items, moduleItems);
- moduleItems.clear();
- return items;
- }
+ constTable.add(runtimeStatement(
+ 'defineLazy(#, { # }, false)', [_constTable, _constLazyAccessors]));
- /// Flattens blocks in [items] to a single list.
- ///
- /// This will not flatten blocks that are marked as being scopes.
- void _copyAndFlattenBlocks(
- List<js_ast.Statement> result, Iterable<js_ast.ModuleItem> items) {
- for (var item in items) {
- if (item is js_ast.Block && !item.isScope) {
- _copyAndFlattenBlocks(result, item.statements);
- } else {
- result.add(item as js_ast.Statement);
- }
+ constTable.addAll(_constTableCache.emit());
}
+ return constTable;
}
js_ast.Fun _emitFunction(FunctionNode f, String name) {
@@ -3729,6 +3767,7 @@
if (!_uriContainer.contains(uri)) {
_uriContainer[uri] = js_ast.LiteralString('"$uri"');
}
+ _uriContainer.setEmitIfIncremental(uri);
return _uriContainer.access(uri);
}
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
index a4c69bd..2758926 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
@@ -13,6 +13,8 @@
show Code, Message, PlainAndColorizedString;
import 'package:dev_compiler/dev_compiler.dart';
+import 'package:dev_compiler/src/compiler/js_names.dart' as js_ast;
+import 'package:dev_compiler/src/compiler/module_builder.dart';
import 'package:dev_compiler/src/js_ast/js_ast.dart' as js_ast;
import 'package:dev_compiler/src/kernel/compiler.dart';
@@ -31,8 +33,6 @@
Member,
Node,
Procedure,
- PropertyGet,
- PropertySet,
RedirectingFactoryConstructor,
TreeNode,
TypeParameter,
@@ -114,7 +114,7 @@
}
DartScope build() {
- if (_offset == null || _library == null || _member == null) return null;
+ if (_offset == null || _library == null) return null;
return DartScope(_library, _cls, _member, _definitions, _typeParameters);
}
@@ -127,7 +127,10 @@
@override
void visitLibrary(Library library) {
_library = library;
- _offset = _component.getOffset(_library.fileUri, _line, _column);
+ _offset = 0;
+ if (_line > 0) {
+ _offset = _component.getOffset(_library.fileUri, _line, _column);
+ }
// Exit early if the evaluation offset is not found.
// Note: the complete scope is not found in this case,
@@ -261,53 +264,6 @@
}
}
-/// Collect private fields and libraries used in expression.
-///
-/// Used during expression evaluation to find symbols
-/// for private fields. The symbols are used in the ddc
-/// compilation of the expression, are not always avalable
-/// in the JavaScript scope, so we need to redefine them.
-///
-/// See [_addSymbolDefinitions]
-class PrivateFieldsVisitor extends Visitor<void> with VisitorVoidMixin {
- final Map<String, Library> privateFields = {};
-
- @override
- void defaultNode(Node node) {
- node.visitChildren(this);
- }
-
- @override
- void visitFieldReference(Field node) {
- if (node.name.isPrivate && !node.isStatic) {
- privateFields[node.name.text] = node.enclosingLibrary;
- }
- }
-
- @override
- void visitField(Field node) {
- if (node.name.isPrivate && !node.isStatic) {
- privateFields[node.name.text] = node.enclosingLibrary;
- }
- }
-
- @override
- void visitPropertyGet(PropertyGet node) {
- var member = node.interfaceTarget;
- if (node.name.isPrivate && member != null && member.isInstanceMember) {
- privateFields[node.name.text] = node.interfaceTarget?.enclosingLibrary;
- }
- }
-
- @override
- void visitPropertySet(PropertySet node) {
- var member = node.interfaceTarget;
- if (node.name.isPrivate && member != null && member.isInstanceMember) {
- privateFields[node.name.text] = node.interfaceTarget?.enclosingLibrary;
- }
- }
-}
-
class ExpressionCompiler {
static final String debugProcedureName = '\$dartEval';
@@ -317,6 +273,7 @@
final IncrementalCompiler _compiler;
final ProgramCompiler _kernel2jsCompiler;
final Component _component;
+ final ModuleFormat _moduleFormat;
DiagnosticMessageHandler onDiagnostic;
@@ -328,6 +285,7 @@
ExpressionCompiler(
this._options,
+ this._moduleFormat,
this.errors,
this._compiler,
this._kernel2jsCompiler,
@@ -494,81 +452,27 @@
// TODO: make this code clear and assumptions enforceable
// https://github.com/dart-lang/sdk/issues/43273
- //
- // We assume here that ExpressionCompiler is always created using
- // onDisgnostic method that adds to the error list that is passed
- // to the same invocation of the ExpressionCompiler constructor.
- // We only use the error list once - below, to detect if the frontend
- // compilation of the expression has failed.
if (errors.isNotEmpty) {
return null;
}
- var jsFun = _kernel2jsCompiler.emitFunctionIncremental(
+ var imports = <js_ast.ModuleItem>[];
+ var jsFun = _kernel2jsCompiler.emitFunctionIncremental(imports,
scope.library, scope.cls, procedure.function, '$debugProcedureName');
_log('Generated JavaScript for expression');
- var jsFunModified = _addSymbolDefinitions(procedure, jsFun, scope);
-
- _log('Added symbol definitions to JavaScript');
-
// print JS ast to string for evaluation
-
var context = js_ast.SimpleJavaScriptPrintingContext();
var opts =
js_ast.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
- jsFunModified.accept(js_ast.Printer(opts, context));
- _log('Performed JavaScript adjustments for expression');
+ var tree = transformFunctionModuleFormat(imports, jsFun, _moduleFormat);
+ tree.accept(
+ js_ast.Printer(opts, context, localNamer: js_ast.TemporaryNamer(tree)));
+
+ _log('Added imports and renamed variables for expression');
return context.getText();
}
-
- /// Add symbol definitions for all symbols in compiled expression
- ///
- /// Example:
- ///
- /// compilation of this._field from library 'main'
- ///
- /// Symbol definition:
- ///
- /// let _f = dart.privateName(main, "_f");
- ///
- /// Expression generated by ddc:
- ///
- /// this[_f]
- ///
- /// TODO: this is a temporary workaround to make JavaScript produced
- /// by the ProgramCompiler self-contained.
- /// Issue: https://github.com/dart-lang/sdk/issues/41480
- js_ast.Fun _addSymbolDefinitions(
- Procedure procedure, js_ast.Fun jsFun, DartScope scope) {
- // get private fields accessed by the evaluated expression
- var fieldsCollector = PrivateFieldsVisitor();
- procedure.accept(fieldsCollector);
- var privateFields = fieldsCollector.privateFields;
-
- // collect library names where private symbols are defined
- var libraryForField = privateFields.map((field, library) =>
- MapEntry(field, _kernel2jsCompiler.emitLibraryName(library).name));
-
- var body = js_ast.Block([
- // re-create private field accessors
- ...libraryForField.keys.map(
- (String field) => _createPrivateField(field, libraryForField[field])),
- // statements generated by the FE
- ...jsFun.body.statements
- ]);
- return js_ast.Fun(jsFun.params, body);
- }
-
- /// Creates a private symbol definition
- ///
- /// example:
- /// let _f = dart.privateName(main, "_f");
- js_ast.Statement _createPrivateField(String field, String library) {
- return js_ast.js.statement('let # = dart.privateName(#, #)',
- [field, library, js_ast.js.string(field)]);
- }
}
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
index 9956b4f..16a64f3 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
@@ -10,6 +10,8 @@
import 'package:args/args.dart';
import 'package:build_integration/file_system/multi_root.dart';
+import 'package:dev_compiler/dev_compiler.dart';
+import 'package:dev_compiler/src/compiler/module_builder.dart';
import 'package:front_end/src/api_prototype/file_system.dart';
import 'package:front_end/src/api_unstable/ddc.dart';
import 'package:kernel/ast.dart' show Component, Library;
@@ -82,11 +84,13 @@
final ProcessedOptions _processedOptions;
final CompilerOptions _compilerOptions;
+ final ModuleFormat _moduleFormat;
final Component _sdkComponent;
ExpressionCompilerWorker._(
this._processedOptions,
this._compilerOptions,
+ this._moduleFormat,
this._sdkComponent,
this.requestStream,
this.sendResponse,
@@ -120,6 +124,9 @@
parseExperimentalArguments(
parsedArgs['enable-experiment'] as List<String>),
onError: (e) => throw e);
+
+ var moduleFormat = parseModuleFormat(parsedArgs['module-format'] as String);
+
return create(
librariesSpecificationUri:
_argToUri(parsedArgs['libraries-file'] as String),
@@ -131,6 +138,7 @@
sdkRoot: _argToUri(parsedArgs['sdk-root'] as String),
trackWidgetCreation: parsedArgs['track-widget-creation'] as bool,
soundNullSafety: parsedArgs['sound-null-safety'] as bool,
+ moduleFormat: moduleFormat,
verbose: parsedArgs['verbose'] as bool,
requestStream: requestStream,
sendResponse: sendResponse,
@@ -152,6 +160,7 @@
Uri sdkRoot,
bool trackWidgetCreation = false,
bool soundNullSafety = false,
+ ModuleFormat moduleFormat = ModuleFormat.amd,
bool verbose = false,
Stream<Map<String, dynamic>> requestStream, // Defaults to read from stdin
void Function(Map<String, dynamic>)
@@ -185,7 +194,7 @@
});
return ExpressionCompilerWorker._(processedOptions, compilerOptions,
- sdkComponent, requestStream, sendResponse)
+ moduleFormat, sdkComponent, requestStream, sendResponse)
.._updateCache(sdkComponent, dartSdkModule, true);
}
@@ -309,6 +318,7 @@
sourceMap: true,
summarizeApi: false,
moduleName: moduleName,
+ soundNullSafety: _compilerOptions.nnbdMode == NnbdMode.Strong,
// Disable asserts due to failures to load source and
// locations on kernel loaded from dill files in DDC.
// https://github.com/dart-lang/sdk/issues/43986
@@ -342,6 +352,7 @@
var expressionCompiler = ExpressionCompiler(
_compilerOptions,
+ _moduleFormat,
errors,
incrementalCompiler,
kernel2jsCompiler,
@@ -673,6 +684,7 @@
..addOption('sdk-root')
..addOption('asset-server-address')
..addOption('asset-server-port')
+ ..addOption('module-format')
..addFlag('track-widget-creation', defaultsTo: false)
..addFlag('sound-null-safety', defaultsTo: false)
..addFlag('verbose', defaultsTo: false);
diff --git a/pkg/dev_compiler/lib/src/kernel/type_table.dart b/pkg/dev_compiler/lib/src/kernel/type_table.dart
index 57b23f0..15cbd31 100644
--- a/pkg/dev_compiler/lib/src/kernel/type_table.dart
+++ b/pkg/dev_compiler/lib/src/kernel/type_table.dart
@@ -9,7 +9,8 @@
import 'package:kernel/kernel.dart';
import '../compiler/js_names.dart' as js_ast;
-import '../compiler/module_containers.dart' show ModuleItemContainer;
+import '../compiler/module_containers.dart'
+ show ModuleItemContainer, ModuleItemData;
import '../js_ast/js_ast.dart' as js_ast;
import '../js_ast/js_ast.dart' show js;
import 'kernel_helpers.dart';
@@ -119,16 +120,26 @@
bool _isNamed(DartType type) =>
typeContainer.contains(type) || _unboundTypeIds.containsKey(type);
+ Set<Library> incrementalLibraries() {
+ var libraries = <Library>{};
+ for (var t in typeContainer.incrementalModuleItems) {
+ if (t is InterfaceType) {
+ libraries.add(t.classNode.enclosingLibrary);
+ }
+ }
+ return libraries;
+ }
+
/// Emit the initializer statements for the type container, which contains
/// all named types with fully bound type parameters.
List<js_ast.Statement> dischargeBoundTypes() {
- for (var t in typeContainer.keys) {
- typeContainer[t] = js.call('() => ((# = #.constFn(#))())',
- [typeContainer.access(t), _runtimeModule, typeContainer[t]]);
+ js_ast.Expression emitValue(DartType t, ModuleItemData data) {
+ var access = js.call('#.#', [data.id, data.jsKey]);
+ return js.call('() => ((# = #.constFn(#))())',
+ [access, _runtimeModule, data.jsValue]);
}
- var boundTypes = typeContainer.incrementalMode
- ? typeContainer.emitIncremental()
- : typeContainer.emit();
+
+ var boundTypes = typeContainer.emit(emitValue: emitValue);
// Bound types should only be emitted once (even across multiple evals).
for (var t in typeContainer.keys) {
typeContainer.setNoEmit(t);
@@ -172,6 +183,7 @@
if (!typeContainer.contains(type)) {
typeContainer[type] = typeRep;
}
+ typeContainer.setEmitIfIncremental(type);
return _unboundTypeIds[type] ?? typeContainer.access(type);
}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
index b335acd..bfec31c 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
@@ -8,7 +8,9 @@
import 'package:cli_util/cli_util.dart';
import 'package:dev_compiler/dev_compiler.dart';
+import 'package:dev_compiler/src/compiler/js_names.dart';
import 'package:dev_compiler/src/compiler/module_builder.dart';
+import 'package:dev_compiler/src/js_ast/js_ast.dart';
import 'package:front_end/src/api_unstable/ddc.dart';
import 'package:front_end/src/compute_platform_binaries_location.dart';
import 'package:front_end/src/fasta/incremental_serializer.dart';
@@ -16,6 +18,7 @@
import 'package:kernel/target/targets.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
+import 'package:vm/transformations/type_flow/utils.dart';
// TODO(annagrin): Replace javascript matching in tests below with evaluating
// the javascript and checking the result.
@@ -74,8 +77,11 @@
final List<String> errors = [];
final CompilerOptions options;
final String dartLangComment;
+ final ModuleFormat moduleFormat;
+ final bool soundNullSafety;
- SetupCompilerOptions(bool soundNullSafety)
+ SetupCompilerOptions(
+ {this.soundNullSafety = true, this.moduleFormat = ModuleFormat.amd})
: options = getOptions(soundNullSafety),
dartLangComment =
soundNullSafety ? dartSoundComment : dartUnsoundComment {
@@ -145,8 +151,12 @@
component.computeCanonicalNames();
// initialize ddc
+ var moduleName = 'foo.dart';
var classHierarchy = compiler.getClassHierarchy();
- var compilerOptions = SharedCompilerOptions(replCompile: true);
+ var compilerOptions = SharedCompilerOptions(
+ replCompile: true,
+ moduleName: moduleName,
+ soundNullSafety: setup.soundNullSafety);
var coreTypes = compiler.getCoreTypes();
final importToSummary = Map<Library, Component>.identity();
@@ -154,16 +164,28 @@
for (var lib in component.libraries) {
importToSummary[lib] = component;
}
- summaryToModule[component] = 'foo.dart';
+ summaryToModule[component] = moduleName;
var kernel2jsCompiler = ProgramCompiler(component, classHierarchy,
compilerOptions, importToSummary, summaryToModule,
coreTypes: coreTypes);
- kernel2jsCompiler.emitModule(component);
+ var moduleTree = kernel2jsCompiler.emitModule(component);
+
+ {
+ var opts = JavaScriptPrintingOptions(
+ allowKeywordsInProperties: true, allowSingleLineIfStatements: true);
+ var printer = SimpleJavaScriptPrintingContext();
+
+ var tree = transformModuleFormat(ModuleFormat.amd, moduleTree);
+ tree.accept(Printer(opts, printer, localNamer: TemporaryNamer(tree)));
+ var printed = printer.getText();
+ debugPrint(printed);
+ }
// create expression compiler
var evaluator = ExpressionCompiler(
setup.options,
+ setup.moduleFormat,
setup.errors,
compiler,
kernel2jsCompiler,
@@ -290,7 +312,62 @@
void main() {
group('Unsound null safety:', () {
- var options = SetupCompilerOptions(false);
+ var options = SetupCompilerOptions(soundNullSafety: false);
+
+ group('Expression compiler extension symbols tests', () {
+ var source = '''
+ ${options.dartLangComment}
+
+ main() {
+ List<int> list = {};
+ list.add(0);
+ /* evaluation placeholder */
+ }
+ ''';
+
+ TestDriver driver;
+
+ setUp(() {
+ driver = TestDriver(options, source);
+ });
+
+ tearDown(() {
+ driver.delete();
+ });
+
+ test('extension symbol used in original compilation', () async {
+ await driver.check(
+ scope: <String, String>{'list': 'list'},
+ expression: 'list.add(1)',
+ expectedResult: r'''
+ (function(list) {
+ const dart_sdk = require('dart_sdk');
+ const dartx = dart_sdk.dartx;
+ var $add = dartx.add;
+ var S = {$add: dartx.add};
+ return list[$add](1);
+ }(
+ list
+ ))
+ ''');
+ });
+
+ test('extension symbol used only in expression compilation', () async {
+ await driver.check(
+ scope: <String, String>{'list': 'list'},
+ expression: 'list.first',
+ expectedResult: r'''
+ (function(list) {
+ const dart_sdk = require('dart_sdk');
+ const dartx = dart_sdk.dartx;
+ var S = {$first: dartx.first};
+ return list[S.$first];
+ }(
+ list
+ ))
+ ''');
+ });
+ });
group('Expression compiler scope collection tests', () {
var source = '''
@@ -360,8 +437,10 @@
await driver.check(
scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
expression: 'global',
- expectedResult: '''
+ expectedResult: r'''
(function(inScope, innerInScope) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.global;
}.bind(this)(
1,
@@ -374,8 +453,10 @@
await driver.check(
scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
expression: 'staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(inScope, innerInScope) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField;
}.bind(this)(
1,
@@ -571,10 +652,23 @@
await driver.check(
scope: <String, String>{'x': '1', 'y': '2', 'z': '3'},
expression: 'main',
- expectedResult: '''
+ expectedResult: r'''
(function(x, y, z) {
- T\$Eval.VoidTodynamic = () => (T\$Eval.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))();
- return dart.fn(foo.main, T\$Eval.VoidTodynamic());
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var T = {
+ VoidTodynamic: () => (T.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))()
+ };
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.fn(foo.main, T.VoidTodynamic());
+ }
+ }, false);
+ var C = [void 0];
+ return C[0] || CT.C0;
}(
1,
2,
@@ -666,6 +760,8 @@
expression: 'x + 1',
expectedResult: '''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.notNull(x) + 1;
}.bind(this)(
1
@@ -677,8 +773,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.C.staticField);
}.bind(this)(
1
@@ -690,8 +790,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.C._staticField);
}.bind(this)(
1
@@ -705,6 +809,8 @@
expression: 'x + field',
expectedResult: '''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.notNull(x) + dart.notNull(this.field);
}.bind(this)(
1
@@ -716,10 +822,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return dart.notNull(x) + dart.notNull(this[_field]);
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return dart.notNull(x) + dart.notNull(this[S._field$1]);
}.bind(this)(
1
))
@@ -730,8 +840,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + global',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.global);
}.bind(this)(
1
@@ -769,8 +883,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '"1234".parseInt()',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo['NumberParsing|parseInt']("1234");
}.bind(this)(
1
@@ -782,10 +898,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return this[_field] = 2;
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return this[S._field$1] = 2;
}.bind(this)(
1
))
@@ -809,8 +929,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C._staticField = 2;
}.bind(this)(
1
@@ -822,8 +944,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}.bind(this)(
1
@@ -885,8 +1009,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.C.staticField);
}.bind(this)(
1
@@ -898,8 +1026,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.C._staticField);
}.bind(this)(
1
@@ -913,6 +1045,8 @@
expression: 'x + field',
expectedResult: '''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.notNull(x) + dart.notNull(this.field);
}.bind(this)(
1
@@ -924,10 +1058,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return dart.notNull(x) + dart.notNull(this[_field]);
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return dart.notNull(x) + dart.notNull(this[S._field$1]);
}.bind(this)(
1
))
@@ -938,10 +1076,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return this[_field] = 2;
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return this[S._field$1] = 2;
}.bind(this)(
1
))
@@ -965,8 +1107,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C._staticField = 2;
}.bind(this)(
1
@@ -978,8 +1122,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}.bind(this)(
1
@@ -1137,8 +1283,10 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C(1,3)',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return new foo.C.new(1, 3);
}(
1,
@@ -1151,10 +1299,14 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C(1,3)._field',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
- let _field = dart.privateName(foo, "_field");
- return new foo.C.new(1, 3)[_field];
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return new foo.C.new(1, 3)[S._field$1];
}(
1,
null
@@ -1166,8 +1318,10 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C.staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField;
}(
1,
@@ -1201,13 +1355,17 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c._field',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
- let _field = dart.privateName(foo, "_field");
- return c[_field];
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return c[S._field$1];
}(
- 1,
- null
+ 1,
+ null
))
''');
});
@@ -1244,8 +1402,10 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: '"1234".parseInt()',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo['NumberParsing|parseInt']("1234");
}(
1,
@@ -1258,10 +1418,14 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c._field = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
- let _field = dart.privateName(foo, "_field");
- return c[_field] = 2;
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return c[S._field$1] = 2;
}(
1,
null
@@ -1294,8 +1458,10 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C.staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}(
1,
@@ -1310,6 +1476,8 @@
expression: 'print(x)',
expectedResult: '''
(function(x, c) {
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
return core.print(x);
}(
1,
@@ -1363,6 +1531,8 @@
expression: r"'$x+$y+$z'",
expectedResult: '''
(function(x, c, y, z) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.str(x) + "+" + dart.str(y) + "+" + dart.str(z);
}(
1,
@@ -1379,6 +1549,8 @@
expression: r"'$y+$z'",
expectedResult: '''
(function(x, c, y, z) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.str(y) + "+" + dart.str(z);
}(
1,
@@ -1438,12 +1610,14 @@
driver.delete();
});
- test('call function using type', () async {
+ test('call function not using type', () async {
await driver.check(
scope: <String, String>{'p': '1'},
expression: 'bar(p)',
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.bar(p);
}(
1
@@ -1455,12 +1629,19 @@
await driver.check(
scope: <String, String>{'p': '0'},
expression: 'baz(p as String)',
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
- T\$Eval.StringL = () => (T\$Eval.StringL = dart.constFn(dart.legacy(core.String)))();
- return foo.baz(T\$Eval.StringL().as(p));
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var T = {
+ StringL: () => (T.StringL = dart.constFn(dart.legacy(core.String)))()
+ };
+ return foo.baz(T.StringL().as(p));
}(
- 0
+ 0
))
''');
});
@@ -1469,12 +1650,24 @@
await driver.check(
scope: <String, String>{'p': '1'},
expression: 'const MyClass(1)',
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
- return dart.const({
- __proto__: foo.MyClass.prototype,
- [_t]: 1
- });
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {MyClass__t: dart.privateName(foo, "MyClass._t")};
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.const({
+ __proto__: foo.MyClass.prototype,
+ [S.MyClass__t]: 1
+ });
+ }
+ }, false);
+ var C = [void 0];
+ return C[0] || CT.C0;
}(
1
))
@@ -1499,8 +1692,10 @@
await driver.check(
scope: <String, String>{'p': '1'},
expression: "Key('t')",
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return new foo.ValueKey.new("t");
}(
1
@@ -1512,12 +1707,24 @@
await driver.check(
scope: <String, String>{'p': '1'},
expression: "const Key('t')",
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
- return dart.const({
- __proto__: foo.ValueKey.prototype,
- [value]: "t"
- });
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {ValueKey_value: dart.privateName(foo, "ValueKey.value")};
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.const({
+ __proto__: foo.ValueKey.prototype,
+ [S.ValueKey_value]: "t"
+ });
+ }
+ }, false);
+ var C = [void 0];
+ return C[0] || CT.C0;
}(
1
))
@@ -1610,6 +1817,8 @@
expression: 'x + 1',
expectedResult: '''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.notNull(x) + 1;
}.bind(this)(
1
@@ -1621,8 +1830,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.C.staticField);
}.bind(this)(
1
@@ -1634,8 +1847,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.C._staticField);
}.bind(this)(
1
@@ -1649,6 +1866,8 @@
expression: 'x + field',
expectedResult: '''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.notNull(x) + dart.notNull(this.field);
}.bind(this)(
1
@@ -1660,10 +1879,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return dart.notNull(x) + dart.notNull(this[_field]);
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return dart.notNull(x) + dart.notNull(this[S._field$1]);
}.bind(this)(
1
))
@@ -1674,8 +1897,12 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + global',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return dart.notNull(x) + dart.notNull(foo.global);
}.bind(this)(
1
@@ -1713,27 +1940,33 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '"1234".parseInt()',
- expectedResult: '''
- (function(x) {
- return foo['NumberParsing|parseInt']("1234");
- }.bind(this)(
- 1
- ))
- ''');
+ expectedResult: r'''
+ (function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return foo['NumberParsing|parseInt']("1234");
+ }.bind(this)(
+ 1
+ ))
+ ''');
});
test('private field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
- expectedResult: '''
- (function(x) {
- let _field = dart.privateName(foo, "_field");
- return this[_field] = 2;
- }.bind(this)(
- 1
- ))
- ''');
+ expectedResult: r'''
+ (function(x) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return this[S._field$1] = 2;
+ }.bind(this)(
+ 1
+ ))
+ ''');
});
test('field modification', () async {
@@ -1753,8 +1986,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C._staticField = 2;
}.bind(this)(
1
@@ -1766,8 +2001,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}.bind(this)(
1
@@ -2047,7 +2284,7 @@
/* evaluation placeholder */
return;
}
-
+
void main() => foo();
''';
@@ -2065,10 +2302,15 @@
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'a is String',
- expectedResult: '''
+ expectedResult: r'''
(function(a, check) {
- T\$Eval.StringL = () => (T\$Eval.StringL = dart.constFn(dart.legacy(core.String)))();
- return T\$Eval.StringL().is(a);
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
+ const dart = dart_sdk.dart;
+ var T = {
+ StringL: () => (T.StringL = dart.constFn(dart.legacy(core.String)))()
+ };
+ return T.StringL().is(a);
}(
null,
null
@@ -2080,9 +2322,15 @@
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'a is int',
- expectedResult: '''
+ expectedResult: r'''
(function(a, check) {
- return T\$Eval.intL().is(a);
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
+ const dart = dart_sdk.dart;
+ var T = {
+ intL: () => (T.intL = dart.constFn(dart.legacy(core.int)))()
+ };
+ return T.intL().is(a);
}(
null,
null
@@ -2096,11 +2344,22 @@
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'const B()',
- expectedResult: '''
+ expectedResult: r'''
(function(a, check) {
- return dart.const({
- __proto__: foo.B.prototype
- });
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.const({
+ __proto__: foo.B.prototype
+ });
+ }
+ }, false);
+ var C = [void 0];
+ return C[0] || CT.C0;
}(
null,
null
@@ -2114,11 +2373,22 @@
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'a == const A()',
- expectedResult: '''
+ expectedResult: r'''
(function(a, check) {
- return dart.equals(a, dart.const({
- __proto__: foo.A.prototype
- }));
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.const({
+ __proto__: foo.A.prototype
+ });
+ }
+ }, false);
+ var C = [void 0];
+ return dart.equals(a, C[0] || CT.C0);
}(
null,
null
@@ -2182,6 +2452,8 @@
expression: 'TType',
expectedResult: '''
(function(TType, KType, a, b) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.wrapType(dart.legacy(TType));
}.bind(this)(
TType,
@@ -2192,10 +2464,112 @@
''');
});
});
+
+ group('Expression compiler tests using extension symbols', () {
+ var source = '''
+ ${options.dartLangComment}
+ void bar() {
+ /* evaluation placeholder */
+ }
+
+ void main() => bar();
+ ''';
+
+ TestDriver driver;
+ setUp(() {
+ driver = TestDriver(options, source);
+ });
+
+ tearDown(() {
+ driver.delete();
+ });
+
+ test('map access', () async {
+ await driver.check(
+ scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+ expression:
+ '(Map<String, String> params) { return params["index"]; }({})',
+ expectedResult: r'''
+ (function() {
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
+ const _js_helper = dart_sdk._js_helper;
+ const dart = dart_sdk.dart;
+ const dartx = dart_sdk.dartx;
+ var T = {
+ StringL: () => (T.StringL = dart.constFn(dart.legacy(core.String)))(),
+ MapOfStringL$StringL: () => (T.MapOfStringL$StringL = dart.constFn(core.Map$(T.StringL(), T.StringL())))(),
+ MapLOfStringL$StringL: () => (T.MapLOfStringL$StringL = dart.constFn(dart.legacy(T.MapOfStringL$StringL())))(),
+ MapLOfStringL$StringLToStringL: () => (T.MapLOfStringL$StringLToStringL = dart.constFn(dart.fnType(T.StringL(), [T.MapLOfStringL$StringL()])))(),
+ IdentityMapOfStringL$StringL: () => (T.IdentityMapOfStringL$StringL = dart.constFn(_js_helper.IdentityMap$(T.StringL(), T.StringL())))()
+ };
+ var S = {$_get: dartx._get};
+ return dart.fn(params => params[S.$_get]("index"), T.MapLOfStringL$StringLToStringL())(new (T.IdentityMapOfStringL$StringL()).new());
+ }(
+
+ ))
+ ''');
+ });
+ });
});
group('Sound null safety:', () {
- var options = SetupCompilerOptions(true);
+ var options = SetupCompilerOptions(soundNullSafety: true);
+
+ group('Expression compiler extension symbols tests', () {
+ var source = '''
+ ${options.dartLangComment}
+
+ main() {
+ List<int> list = {};
+ list.add(0);
+ /* evaluation placeholder */
+ }
+ ''';
+
+ TestDriver driver;
+
+ setUp(() {
+ driver = TestDriver(options, source);
+ });
+
+ tearDown(() {
+ driver.delete();
+ });
+
+ test('extension symbol used in original compilation', () async {
+ await driver.check(
+ scope: <String, String>{'list': 'list'},
+ expression: 'list.add(1)',
+ expectedResult: r'''
+ (function(list) {
+ const dart_sdk = require('dart_sdk');
+ const dartx = dart_sdk.dartx;
+ var $add = dartx.add;
+ var S = {$add: dartx.add};
+ return list[$add](1);
+ }(
+ list
+ ))
+ ''');
+ });
+
+ test('extension symbol used only in expression compilation', () async {
+ await driver.check(
+ scope: <String, String>{'list': 'list'},
+ expression: 'list.first',
+ expectedResult: r'''
+ (function(list) {
+ const dart_sdk = require('dart_sdk');
+ const dartx = dart_sdk.dartx;
+ var S = {$first: dartx.first};
+ return list[S.$first];
+ }(
+ list
+ ))
+ ''');
+ });
+ });
group('Expression compiler scope collection tests', () {
var source = '''
@@ -2265,8 +2639,10 @@
await driver.check(
scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
expression: 'global',
- expectedResult: '''
+ expectedResult: r'''
(function(inScope, innerInScope) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.global;
}.bind(this)(
1,
@@ -2279,8 +2655,10 @@
await driver.check(
scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
expression: 'staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(inScope, innerInScope) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField;
}.bind(this)(
1,
@@ -2476,10 +2854,23 @@
await driver.check(
scope: <String, String>{'x': '1', 'y': '2', 'z': '3'},
expression: 'main',
- expectedResult: '''
+ expectedResult: r'''
(function(x, y, z) {
- T\$Eval.VoidTodynamic = () => (T\$Eval.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))();
- return dart.fn(foo.main, T\$Eval.VoidTodynamic());
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var T = {
+ VoidTodynamic: () => (T.VoidTodynamic = dart.constFn(dart.fnType(dart.dynamic, [])))()
+ };
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.fn(foo.main, T.VoidTodynamic());
+ }
+ }, false);
+ var C = [void 0];
+ return C[0] || CT.C0;
}(
1,
2,
@@ -2571,7 +2962,7 @@
expression: 'x + 1',
expectedResult: '''
(function(x) {
- return dart.notNull(x) + 1;
+ return x + 1;
}.bind(this)(
1
))
@@ -2582,9 +2973,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.C.staticField);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.C.staticField;
}.bind(this)(
1
))
@@ -2595,9 +2988,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.C._staticField);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.C._staticField;
}.bind(this)(
1
))
@@ -2610,7 +3005,7 @@
expression: 'x + field',
expectedResult: '''
(function(x) {
- return dart.notNull(x) + dart.notNull(this.field);
+ return x + this.field;
}.bind(this)(
1
))
@@ -2621,10 +3016,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return dart.notNull(x) + dart.notNull(this[_field]);
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return x + this[S._field$1];
}.bind(this)(
1
))
@@ -2635,9 +3034,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + global',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.global);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.global;
}.bind(this)(
1
))
@@ -2674,8 +3075,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '"1234".parseInt()',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo['NumberParsing|parseInt']("1234");
}.bind(this)(
1
@@ -2687,10 +3090,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return this[_field] = 2;
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return this[S._field$1] = 2;
}.bind(this)(
1
))
@@ -2714,8 +3121,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C._staticField = 2;
}.bind(this)(
1
@@ -2727,8 +3136,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}.bind(this)(
1
@@ -2790,9 +3201,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.C.staticField);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.C.staticField;
}.bind(this)(
1
))
@@ -2803,9 +3216,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.C._staticField);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.C._staticField;
}.bind(this)(
1
))
@@ -2818,7 +3233,7 @@
expression: 'x + field',
expectedResult: '''
(function(x) {
- return dart.notNull(x) + dart.notNull(this.field);
+ return x + this.field;
}.bind(this)(
1
))
@@ -2829,10 +3244,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return dart.notNull(x) + dart.notNull(this[_field]);
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return x + this[S._field$1];
}.bind(this)(
1
))
@@ -2843,10 +3262,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return this[_field] = 2;
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return this[S._field$1] = 2;
}.bind(this)(
1
))
@@ -2870,8 +3293,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C._staticField = 2;
}.bind(this)(
1
@@ -2883,8 +3308,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}.bind(this)(
1
@@ -3042,24 +3469,30 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C(1,3)',
- expectedResult: '''
- (function(x, c) {
- return new foo.C.new(1, 3);
- }(
- 1,
- null
- ))
- ''');
+ expectedResult: r'''
+ (function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return new foo.C.new(1, 3);
+ }(
+ 1,
+ null
+ ))
+ ''');
});
test('access field of new object', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C(1,3)._field',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
- let _field = dart.privateName(foo, "_field");
- return new foo.C.new(1, 3)[_field];
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return new foo.C.new(1, 3)[S._field$1];
}(
1,
null
@@ -3071,8 +3504,10 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C.staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField;
}(
1,
@@ -3106,10 +3541,14 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c._field',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
- let _field = dart.privateName(foo, "_field");
- return c[_field];
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return c[S._field$1];
}(
1,
null
@@ -3149,8 +3588,10 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: '"1234".parseInt()',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo['NumberParsing|parseInt']("1234");
}(
1,
@@ -3163,10 +3604,14 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c._field = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
- let _field = dart.privateName(foo, "_field");
- return c[_field] = 2;
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return c[S._field$1] = 2;
}(
1,
null
@@ -3199,8 +3644,10 @@
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C.staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x, c) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}(
1,
@@ -3215,6 +3662,8 @@
expression: 'print(x)',
expectedResult: '''
(function(x, c) {
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
return core.print(x);
}(
1,
@@ -3268,6 +3717,8 @@
expression: r"'$x+$y+$z'",
expectedResult: '''
(function(x, c, y, z) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.str(x) + "+" + dart.str(y) + "+" + dart.str(z);
}(
1,
@@ -3284,6 +3735,8 @@
expression: r"'$y+$z'",
expectedResult: '''
(function(x, c, y, z) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.str(y) + "+" + dart.str(z);
}(
1,
@@ -3347,8 +3800,10 @@
await driver.check(
scope: <String, String>{'p': '1'},
expression: 'bar(p)',
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.bar(p);
}(
1
@@ -3360,8 +3815,12 @@
await driver.check(
scope: <String, String>{'p': '0'},
expression: 'baz(p as String)',
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.baz(core.String.as(p));
}(
0
@@ -3373,12 +3832,24 @@
await driver.check(
scope: <String, String>{'p': '1'},
expression: 'const MyClass(1)',
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
- return dart.const({
- __proto__: foo.MyClass.prototype,
- [_t]: 1
- });
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {MyClass__t: dart.privateName(foo, "MyClass._t")};
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.const({
+ __proto__: foo.MyClass.prototype,
+ [S.MyClass__t]: 1
+ });
+ }
+ }, false);
+ var C = [void 0];
+ return C[0] || CT.C0;
}(
1
))
@@ -3403,8 +3874,10 @@
await driver.check(
scope: <String, String>{'p': '1'},
expression: "Key('t')",
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return new foo.ValueKey.new("t");
}(
1
@@ -3416,12 +3889,24 @@
await driver.check(
scope: <String, String>{'p': '1'},
expression: "const Key('t')",
- expectedResult: '''
+ expectedResult: r'''
(function(p) {
- return dart.const({
- __proto__: foo.ValueKey.prototype,
- [value]: "t"
- });
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {ValueKey_value: dart.privateName(foo, "ValueKey.value")};
+ const CT = Object.create(null);
+ dart.defineLazy(CT, {
+ get C0() {
+ return C[0] = dart.const({
+ __proto__: foo.ValueKey.prototype,
+ [S.ValueKey_value]: "t"
+ });
+ }
+ }, false);
+ var C = [void 0];
+ return C[0] || CT.C0;
}(
1
))
@@ -3514,7 +3999,7 @@
expression: 'x + 1',
expectedResult: '''
(function(x) {
- return dart.notNull(x) + 1;
+ return x + 1;
}.bind(this)(
1
))
@@ -3525,9 +4010,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.C.staticField);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.C.staticField;
}.bind(this)(
1
))
@@ -3538,9 +4025,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.C._staticField);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.C._staticField;
}.bind(this)(
1
))
@@ -3553,7 +4042,7 @@
expression: 'x + field',
expectedResult: '''
(function(x) {
- return dart.notNull(x) + dart.notNull(this.field);
+ return x + this.field;
}.bind(this)(
1
))
@@ -3564,10 +4053,14 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return dart.notNull(x) + dart.notNull(this[_field]);
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return x + this[S._field$1];
}.bind(this)(
1
))
@@ -3578,9 +4071,11 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + global',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- return dart.notNull(x) + dart.notNull(foo.global);
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return x + foo.global;
}.bind(this)(
1
))
@@ -3617,23 +4112,29 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '"1234".parseInt()',
- expectedResult: '''
- (function(x) {
- return foo['NumberParsing|parseInt']("1234");
- }.bind(this)(
- 1
- ))
- ''');
+ expectedResult: r'''
+ (function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ return foo['NumberParsing|parseInt']("1234");
+ }.bind(this)(
+ 1
+ ))
+ ''');
});
test('private field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
- let _field = dart.privateName(foo, "_field");
- return this[_field] = 2;
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
+ var S = {_field$1: dart.privateName(foo, "_field")};
+ return this[S._field$1] = 2;
}.bind(this)(
1
))
@@ -3657,8 +4158,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C._staticField = 2;
}.bind(this)(
1
@@ -3670,8 +4173,10 @@
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
- expectedResult: '''
+ expectedResult: r'''
(function(x) {
+ const foo$46dart = require('foo.dart');
+ const foo = foo$46dart.foo;
return foo.C.staticField = 2;
}.bind(this)(
1
@@ -3990,6 +4495,8 @@
expression: 'TType',
expectedResult: '''
(function(TType, KType, a, b) {
+ const dart_sdk = require('dart_sdk');
+ const dart = dart_sdk.dart;
return dart.wrapType(TType);
}.bind(this)(
TType,
@@ -4000,5 +4507,51 @@
''');
});
});
+
+ group('Expression compiler tests using extension symbols', () {
+ var source = '''
+ ${options.dartLangComment}
+ void bar() {
+ /* evaluation placeholder */
+ }
+
+ void main() => bar();
+ ''';
+
+ TestDriver driver;
+ setUp(() {
+ driver = TestDriver(options, source);
+ });
+
+ tearDown(() {
+ driver.delete();
+ });
+
+ test('map access', () async {
+ await driver.check(
+ scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+ expression:
+ '(Map<String, String> params) { return params["index"]; }({})',
+ expectedResult: r'''
+ (function() {
+ const dart_sdk = require('dart_sdk');
+ const core = dart_sdk.core;
+ const _js_helper = dart_sdk._js_helper;
+ const dart = dart_sdk.dart;
+ const dartx = dart_sdk.dartx;
+ var T = {
+ StringN: () => (T.StringN = dart.constFn(dart.nullable(core.String)))(),
+ MapOfString$String: () => (T.MapOfString$String = dart.constFn(core.Map$(core.String, core.String)))(),
+ MapOfString$StringToStringN: () => (T.MapOfString$StringToStringN = dart.constFn(dart.fnType(T.StringN(), [T.MapOfString$String()])))(),
+ IdentityMapOfString$String: () => (T.IdentityMapOfString$String = dart.constFn(_js_helper.IdentityMap$(core.String, core.String)))()
+ };
+ var S = {$_get: dartx._get};
+ return dart.fn(params => params[S.$_get]("index"), T.MapOfString$StringToStringN())(new (T.IdentityMapOfString$String()).new());
+ }(
+
+ ))
+ ''');
+ });
+ });
});
}
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index a4208db..139e557 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -10,7 +10,7 @@
import 'package:args/args.dart';
import 'package:dev_compiler/dev_compiler.dart'
- show DevCompilerTarget, ExpressionCompiler;
+ show DevCompilerTarget, ExpressionCompiler, parseModuleFormat;
// front_end/src imports below that require lint `ignore_for_file`
// are a temporary state of things until frontend team builds better api
@@ -605,6 +605,7 @@
String filename, String fileSystemScheme, String moduleFormat) async {
var packageConfig = await loadPackageConfigUri(
_compilerOptions.packagesFileUri ?? File('.packages').absolute.uri);
+ var soundNullSafety = _compilerOptions.nnbdMode == NnbdMode.Strong;
final Component component = results.component;
// Compute strongly connected components.
final strongComponents = StrongComponents(component,
@@ -623,7 +624,8 @@
component, strongComponents, fileSystemScheme, packageConfig,
useDebuggerModuleNames: useDebuggerModuleNames,
emitDebugMetadata: emitDebugMetadata,
- moduleFormat: moduleFormat);
+ moduleFormat: moduleFormat,
+ soundNullSafety: soundNullSafety);
final sourceFileSink = sourceFile.openWrite();
final manifestFileSink = manifestFile.openWrite();
final sourceMapsFileSink = sourceMapsFile.openWrite();
@@ -839,6 +841,7 @@
var expressionCompiler = new ExpressionCompiler(
_compilerOptions,
+ parseModuleFormat(_options['dartdevc-module-format'] as String),
errors,
_generator.generator,
kernel2jsCompiler,
diff --git a/pkg/frontend_server/lib/src/javascript_bundle.dart b/pkg/frontend_server/lib/src/javascript_bundle.dart
index 924c551..346b4d8 100644
--- a/pkg/frontend_server/lib/src/javascript_bundle.dart
+++ b/pkg/frontend_server/lib/src/javascript_bundle.dart
@@ -27,6 +27,7 @@
this._fileSystemScheme, this._packageConfig,
{this.useDebuggerModuleNames = false,
this.emitDebugMetadata = false,
+ this.soundNullSafety = false,
String moduleFormat})
: compilers = <String, ProgramCompiler>{},
_moduleFormat = parseModuleFormat(moduleFormat ?? 'amd') {
@@ -65,6 +66,7 @@
final bool emitDebugMetadata;
final Map<String, ProgramCompiler> compilers;
final ModuleFormat _moduleFormat;
+ final bool soundNullSafety;
List<Component> _summaries;
List<Uri> _summaryUris;
@@ -135,6 +137,7 @@
summarizeApi: false,
emitDebugMetadata: emitDebugMetadata,
moduleName: moduleName,
+ soundNullSafety: soundNullSafety,
),
importToSummary,
summaryToModule,
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index e4807d5..5f89dfd 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -124,12 +124,28 @@
virtual void VisitTypedDataViewPointers(TypedDataViewPtr view,
ObjectPtr* first,
ObjectPtr* last) {
- // First we forward all fields of the typed data view.
+ // TypedDataViews require extra processing to update their
+ // PointerBase::data_ pointer. If the underlying typed data is external, no
+ // update is needed. If the underlying typed data is internal, the pointer
+ // must be updated if the typed data was copied or promoted. We cannot
+ // safely dereference the underlying typed data to make this distinction.
+ // It may have been forwarded by a different scavanger worker, so the access
+ // could have a data race. Rather than checking the CID of the underlying
+ // typed data, which requires dereferencing the copied/promoted header, we
+ // compare the view's internal pointer to what it should be if the
+ // underlying typed data was internal, and assume that external typed data
+ // never points into the Dart heap. We must do this before VisitPointers
+ // because we want to compare the old pointer and old typed data.
+ const bool is_external =
+ view->untag()->data_ != view->untag()->DataFieldForInternalTypedData();
+
+ // Forward all fields of the typed data view.
VisitPointers(first, last);
if (view->untag()->data_ == nullptr) {
ASSERT(RawSmiValue(view->untag()->offset_in_bytes_) == 0 &&
RawSmiValue(view->untag()->length_) == 0);
+ ASSERT(is_external);
return;
}
@@ -146,17 +162,21 @@
*reinterpret_cast<uword*>(UntaggedObject::ToAddr(td));
ASSERT(!IsForwarding(td_header) || td->IsOldObject());
- // We can always obtain the class id from the forwarded backing store.
- const classid_t cid = td->GetClassId();
+ if (!parallel) {
+ // When there is only one worker, there is no data race.
+ ASSERT_EQUAL(IsExternalTypedDataClassId(td->GetClassId()), is_external);
+ }
// If we have external typed data we can simply return since the backing
// store lives in C-heap and will not move.
- if (IsExternalTypedDataClassId(cid)) {
+ if (is_external) {
return;
}
// Now we update the inner pointer.
- ASSERT(IsTypedDataClassId(cid));
+ if (!parallel) {
+ ASSERT(IsTypedDataClassId(td->GetClassId()));
+ }
view->untag()->RecomputeDataFieldForInternalTypedData();
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 4d42361..af6332c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -16513,7 +16513,7 @@
for (intptr_t i = 0; i < length_; i++) {
comments_[i].pc_offset = comments.PCOffsetAt(i);
comments_[i].comment =
- Utils::CreateCStringUniquePtr(strdup(comments.CommentAt(i)));
+ Utils::CreateCStringUniquePtr(Utils::StrDup(comments.CommentAt(i)));
}
}
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 56a71b7..82c1998 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -2646,18 +2646,22 @@
data_ = payload + offset_in_bytes;
}
- // Recopute [data_] based on internal [typed_data_] - needs to be called by GC
- // whenever the backing store moved.
+ // Recompute [data_] based on internal [typed_data_] - needs to be called by
+ // GC whenever the backing store moved.
//
// NOTICE: This method assumes [this] is the forwarded object and the
// [typed_data_] pointer points to the new backing store. The backing store's
// fields don't need to be valid - only it's address.
void RecomputeDataFieldForInternalTypedData() {
+ data_ = DataFieldForInternalTypedData();
+ }
+
+ uint8_t* DataFieldForInternalTypedData() const {
const intptr_t offset_in_bytes = RawSmiValue(offset_in_bytes_);
uint8_t* payload =
reinterpret_cast<uint8_t*>(UntaggedObject::ToAddr(typed_data()) +
UntaggedTypedData::payload_offset());
- data_ = payload + offset_in_bytes;
+ return payload + offset_in_bytes;
}
void ValidateInnerPointer() {
diff --git a/tests/corelib/unsigned_shift_test.dart b/tests/corelib/unsigned_shift_test.dart
index c190ff3..ab3220f 100644
--- a/tests/corelib/unsigned_shift_test.dart
+++ b/tests/corelib/unsigned_shift_test.dart
@@ -13,7 +13,7 @@
main() {
testIntegerShifts();
- testNonDoubleShifts();
+ testNonIntegerShifts();
testConstantShifts();
}
@@ -47,18 +47,18 @@
// If so, it is zero when converted to a fixed precision.
if (double.infinity is int) {
int number = (double.infinity as int);
- Expect.equals(0, number >> 1);
- Expect.throws(() => 1 >>> number); // infinity > 64.
+ Expect.equals(0, number >>> 1);
+ Expect.equals(0, 1 >>> number); // infinity > 64.
}
}
-void testNonDoubleShifts() {
+void testNonIntegerShifts() {
double n = 0.0;
n >>> 1; //# 01: compile-time error
for (dynamic number in [0.0, 1.0, 2.4, -2.4, double.infinity, double.nan]) {
if (number is! int) {
- Expect.throws(() => number >>> 1);
- Expect.throws(() => 1 >>> number);
+ Expect.throws(() => number >>> 1); //# 07: ok
+ Expect.throws(() => 1 >>> number); //# 08: ok
}
}
}
diff --git a/tests/corelib_2/unsigned_shift_test.dart b/tests/corelib_2/unsigned_shift_test.dart
index c190ff3..ab3220f 100644
--- a/tests/corelib_2/unsigned_shift_test.dart
+++ b/tests/corelib_2/unsigned_shift_test.dart
@@ -13,7 +13,7 @@
main() {
testIntegerShifts();
- testNonDoubleShifts();
+ testNonIntegerShifts();
testConstantShifts();
}
@@ -47,18 +47,18 @@
// If so, it is zero when converted to a fixed precision.
if (double.infinity is int) {
int number = (double.infinity as int);
- Expect.equals(0, number >> 1);
- Expect.throws(() => 1 >>> number); // infinity > 64.
+ Expect.equals(0, number >>> 1);
+ Expect.equals(0, 1 >>> number); // infinity > 64.
}
}
-void testNonDoubleShifts() {
+void testNonIntegerShifts() {
double n = 0.0;
n >>> 1; //# 01: compile-time error
for (dynamic number in [0.0, 1.0, 2.4, -2.4, double.infinity, double.nan]) {
if (number is! int) {
- Expect.throws(() => number >>> 1);
- Expect.throws(() => 1 >>> number);
+ Expect.throws(() => number >>> 1); //# 07: ok
+ Expect.throws(() => 1 >>> number); //# 08: ok
}
}
}
diff --git a/tests/dartdevc/container_test.dart b/tests/dartdevc/container_test.dart
deleted file mode 100644
index 54d2165..0000000
--- a/tests/dartdevc/container_test.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// Tests that evaluation containers aren't renamed by DDC.
-
-import 'dart:_foreign_helper' as helper show JS;
-
-import 'package:expect/expect.dart';
-
-class T {
- final int T$Eval = 0;
- final int S$Eval = 0;
- String get realT$Eval => helper.JS<String>('', 'T\$Eval.toString()');
- String get realS$Eval => helper.JS<String>('', 'S\$Eval.toString()');
-}
-
-class T$Eval {}
-
-void main() {
- var T$Eval = T();
- var S$Eval = T$Eval;
-
- var container1 = helper.JS<String>('', 'T\$Eval.toString()');
- var container2 = helper.JS<String>('', 'S\$Eval.toString()');
-
- // Evaluation containers are JS Objects. Ensure they aren't shadowed by JS
- // symbols or Dart constructs.
- Expect.equals('[object Object]', '$container1');
- Expect.equals('[object Object]', '$container2');
-
- Expect.equals("Instance of 'T'", T$Eval.toString());
- Expect.equals(T$Eval.T$Eval, 0);
- Expect.equals(T$Eval.S$Eval, 0);
- Expect.notEquals(T$Eval.toString(), container1);
- Expect.equals(T$Eval.realT$Eval, container1);
- Expect.equals(T$Eval.realS$Eval, container2);
-}
diff --git a/tools/VERSION b/tools/VERSION
index 150d415..b39a857 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 121
+PRERELEASE 122
PRERELEASE_PATCH 0
\ No newline at end of file