Version 2.14.0-221.0.dev

Merge commit 'dea0488efedbd10a913489e35f52d445160c92b9' into 'dev'
diff --git a/pkg/dev_compiler/lib/src/compiler/js_names.dart b/pkg/dev_compiler/lib/src/compiler/js_names.dart
index 3295e0c..186a316 100644
--- a/pkg/dev_compiler/lib/src/compiler/js_names.dart
+++ b/pkg/dev_compiler/lib/src/compiler/js_names.dart
@@ -74,6 +74,17 @@
   void visitChildren(NodeVisitor visitor) => _expr.visitChildren(visitor);
 }
 
+/// Provides a mechanism to listen for naming choices when `Identifier` nodes
+/// are compiled into an actual name in the JavaScript.
+class NameListener {
+  /// A mapping of all name selections that were made.
+  final identifierNames = <Identifier, String>{};
+
+  /// Signals that [name] was selected to represent [identifier].
+  void nameSelected(Identifier identifier, String name) =>
+      identifierNames[identifier] = name;
+}
+
 /// This class has two purposes:
 ///
 /// * rename JS identifiers to avoid keywords.
@@ -88,13 +99,20 @@
 class TemporaryNamer extends LocalNamer {
   _FunctionScope scope;
 
-  TemporaryNamer(Node node) : scope = _RenameVisitor.build(node).rootScope;
+  /// Listener to be notified when a name is selected (rename or not) for an
+  /// `Identifier`.
+  ///
+  /// Can be `null` when there is no listener attached.
+  final NameListener _nameListener;
+
+  TemporaryNamer(Node node, [this._nameListener])
+      : scope = _RenameVisitor.build(node).rootScope;
 
   @override
   String getName(Identifier node) {
-    var rename = scope.renames[identifierKey(node)];
-    if (rename != null) return rename;
-    return node.name;
+    var name = scope.renames[identifierKey(node)] ?? node.name;
+    _nameListener?.nameSelected(node, name);
+    return name;
   }
 
   @override
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index 2bac27d..fc521de 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -11,7 +11,6 @@
 import 'package:args/args.dart';
 import 'package:build_integration/file_system/multi_root.dart';
 import 'package:cli_util/cli_util.dart' show getSdkPath;
-import 'package:dev_compiler/src/kernel/module_symbols.dart';
 import 'package:front_end/src/api_unstable/ddc.dart' as fe;
 import 'package:kernel/binary/ast_to_binary.dart' as kernel show BinaryPrinter;
 import 'package:kernel/class_hierarchy.dart';
@@ -31,6 +30,8 @@
 import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
 import 'compiler.dart';
 import 'module_metadata.dart';
+import 'module_symbols.dart';
+import 'module_symbols_collector.dart';
 import 'target.dart';
 
 const _binaryName = 'dartdevc -k';
@@ -682,8 +683,9 @@
   }
 
   var tree = transformModuleFormat(format, moduleTree);
-  var namer = js_ast.TemporaryNamer(tree);
-  tree.accept(js_ast.Printer(opts, printer, localNamer: namer));
+  var nameListener = emitDebugSymbols ? js_ast.NameListener() : null;
+  tree.accept(js_ast.Printer(opts, printer,
+      localNamer: js_ast.TemporaryNamer(tree, nameListener)));
 
   Map builtMap;
   if (buildSourceMap && sourceMap != null) {
@@ -726,15 +728,27 @@
       ? _emitMetadata(moduleTree, component, mapUrl, jsUrl, fullDillUri)
       : null;
 
-  var debugSymbols =
-      emitDebugSymbols ? _emitSymbols(compiler, component) : null;
+  var debugSymbols = emitDebugSymbols
+      ? _emitSymbols(compiler, nameListener.identifierNames, component)
+      : null;
 
   return JSCode(text, builtMap, symbols: debugSymbols, metadata: debugMetadata);
 }
 
-ModuleSymbols _emitSymbols(ProgramCompiler compiler, Component component) {
-  // TODO(annagrin): collect module symbols.
-  return ModuleSymbols();
+/// Assembles symbol information describing the nodes from the AST [component]
+/// and their representation in JavaScript.
+///
+/// Uses information from the [compiler] used to compile the JS module combined
+/// with [identifierNames] that maps JavaScript identifier nodes to their actual
+/// names used when outputting the JavaScript.
+ModuleSymbols _emitSymbols(ProgramCompiler compiler,
+    Map<js_ast.Identifier, String> identifierNames, Component component) {
+  var classJsNames = <Class, String>{
+    for (var e in compiler.classIdentifiers.entries)
+      e.key: identifierNames[e.value],
+  };
+
+  return ModuleSymbolsCollector(classJsNames).collectSymbolInfo(component);
 }
 
 ModuleMetadata _emitMetadata(js_ast.Program program, Component component,
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 0d34217..e6d2dbf 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -46,6 +46,13 @@
         DartTypeVisitor<js_ast.Expression> {
   final SharedCompilerOptions _options;
 
+  /// Maps each `Class` node compiled in the module to the `Identifier`s used to
+  /// name the class in JavaScript.
+  ///
+  /// This mapping is used when generating the symbol information for the
+  /// module.
+  final classIdentifiers = <Class, js_ast.Identifier>{};
+
   /// Maps a library URI import, that is not in [_libraries], to the
   /// corresponding Kernel summary module we imported it with.
   ///
@@ -781,12 +788,15 @@
   js_ast.Statement _emitClassStatement(Class c, js_ast.Expression className,
       js_ast.Expression heritage, List<js_ast.Method> methods) {
     if (c.typeParameters.isNotEmpty) {
-      return js_ast.ClassExpression(
-              className as js_ast.Identifier, heritage, methods)
+      var classIdentifier = className as js_ast.Identifier;
+      if (_options.emitDebugSymbols) classIdentifiers[c] = classIdentifier;
+      return js_ast.ClassExpression(classIdentifier, heritage, methods)
           .toStatement();
     }
-    var classExpr = js_ast.ClassExpression(
-        _emitTemporaryId(getLocalClassName(c)), heritage, methods);
+
+    var classIdentifier = _emitTemporaryId(getLocalClassName(c));
+    if (_options.emitDebugSymbols) classIdentifiers[c] = classIdentifier;
+    var classExpr = js_ast.ClassExpression(classIdentifier, heritage, methods);
     return js.statement('# = #;', [className, classExpr]);
   }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/module_symbols_collector.dart b/pkg/dev_compiler/lib/src/kernel/module_symbols_collector.dart
new file mode 100644
index 0000000..bf405a2
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/kernel/module_symbols_collector.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'package:kernel/kernel.dart';
+
+import 'module_symbols.dart';
+
+class ModuleSymbolsCollector extends RecursiveVisitor {
+  final scopes = <ScopeSymbol>[];
+
+  final moduleSymbols = ModuleSymbols(
+    // TODO version
+    // TODO moduleName
+    libraries: <LibrarySymbol>[],
+    scripts: <Script>[],
+    classes: <ClassSymbol>[],
+    // TODO functionTypes
+    // TODO functions
+    // TODO scopes
+    // TODO variables
+  );
+
+  final Map<Class, String> classJsNames;
+
+  ModuleSymbolsCollector(this.classJsNames);
+
+  ModuleSymbols collectSymbolInfo(Component node) {
+    node.accept(this);
+    return moduleSymbols;
+  }
+
+  /// Returns the id of the script in this module with the matching [fileUri].
+  String _scriptId(Uri fileUri) => fileUri.toString();
+
+  @override
+  void visitClass(Class node) {
+    var classSymbol = ClassSymbol(
+        name: node.name,
+        isAbstract: node.isAbstract,
+        // TODO isConst - has a const constructor?
+        superClassId: classJsNames[node.superclass],
+        interfaceIds: [
+          for (var type in node.implementedTypes) classJsNames[type.classNode]
+        ],
+        typeParameters: {
+          for (var param in node.typeParameters)
+            param.name: param.name // TODO: value should be JS name
+        },
+        localId: classJsNames[node],
+        scopeId: scopes.last.id,
+        location: SourceLocation(
+            scriptId: _scriptId(node.location.file),
+            tokenPos: node.startFileOffset,
+            endTokenPos: node.fileEndOffset),
+        // Create empty list, they are added in visitField().
+        variableIds: <String>[],
+        scopeIds: <String>[]);
+
+    scopes.add(classSymbol);
+    node.visitChildren(this);
+    scopes
+      ..removeLast()
+      ..last.scopeIds.add(classSymbol.id);
+    moduleSymbols.classes.add(classSymbol);
+  }
+
+  @override
+  void visitLibrary(Library node) {
+    var librarySymbol = LibrarySymbol(
+        name: node.name,
+        uri: node.importUri.toString(),
+        dependencies: [
+          for (var dep in node.dependencies)
+            LibrarySymbolDependency(
+                isImport: dep.isImport,
+                isDeferred: dep.isDeferred,
+                // TODO prefix
+                targetId: dep.targetLibrary.importUri.toString())
+        ],
+        variableIds: <String>[],
+        scopeIds: <String>[]);
+
+    // TODO: Save some space by using integers as local ids?
+    var scripts = [
+      Script(
+          uri: node.fileUri.toString(),
+          localId: _scriptId(node.fileUri),
+          libraryId: librarySymbol.id),
+      for (var part in node.parts)
+        Script(
+            uri: node.fileUri.resolve(part.partUri).toString(),
+            localId: _scriptId(node.fileUri.resolve(part.partUri)),
+            libraryId: librarySymbol.id),
+    ];
+
+    librarySymbol.scriptIds = [for (var script in scripts) script.id];
+    moduleSymbols.scripts.addAll(scripts);
+
+    scopes.add(librarySymbol);
+    node.visitChildren(this);
+    scopes.removeLast();
+    moduleSymbols.libraries.add(librarySymbol);
+  }
+}
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 d5fbeea..4a98206 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
@@ -6,102 +6,20 @@
 
 import 'dart:io' show Directory, File;
 
-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';
 import 'package:kernel/ast.dart' show Component, Library;
-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';
 
+import '../shared_test_options.dart';
+
 // TODO(annagrin): Replace javascript matching in tests below with evaluating
 // the javascript and checking the result.
 // See https://github.com/dart-lang/sdk/issues/41959
 
-class DevelopmentIncrementalCompiler extends IncrementalCompiler {
-  Uri entryPoint;
-
-  DevelopmentIncrementalCompiler(CompilerOptions options, this.entryPoint,
-      [Uri initializeFrom,
-      bool outlineOnly,
-      IncrementalSerializer incrementalSerializer])
-      : super(
-            CompilerContext(
-                ProcessedOptions(options: options, inputs: [entryPoint])),
-            initializeFrom,
-            outlineOnly,
-            incrementalSerializer);
-
-  DevelopmentIncrementalCompiler.fromComponent(CompilerOptions options,
-      this.entryPoint, Component componentToInitializeFrom,
-      [bool outlineOnly, IncrementalSerializer incrementalSerializer])
-      : super.fromComponent(
-            CompilerContext(
-                ProcessedOptions(options: options, inputs: [entryPoint])),
-            componentToInitializeFrom,
-            outlineOnly,
-            incrementalSerializer);
-}
-
-class SetupCompilerOptions {
-  static final sdkRoot = computePlatformBinariesLocation();
-  static final sdkUnsoundSummaryPath = p.join(sdkRoot.path, 'ddc_sdk.dill');
-  static final sdkSoundSummaryPath =
-      p.join(sdkRoot.path, 'ddc_outline_sound.dill');
-  static final librariesSpecificationUri =
-      p.join(p.dirname(p.dirname(getSdkPath())), 'libraries.json');
-
-  static CompilerOptions getOptions(bool soundNullSafety) {
-    var options = CompilerOptions()
-      ..verbose = false // set to true for debugging
-      ..sdkRoot = sdkRoot
-      ..target = DevCompilerTarget(TargetFlags())
-      ..librariesSpecificationUri = Uri.base.resolve('sdk/lib/libraries.json')
-      ..omitPlatform = true
-      ..sdkSummary = sdkRoot.resolve(
-          soundNullSafety ? sdkSoundSummaryPath : sdkUnsoundSummaryPath)
-      ..environmentDefines = const {}
-      ..nnbdMode = soundNullSafety ? NnbdMode.Strong : NnbdMode.Weak;
-    return options;
-  }
-
-  static final String dartUnsoundComment = '// @dart = 2.9';
-  static final String dartSoundComment = '//';
-
-  final List<String> errors = [];
-  final CompilerOptions options;
-  final String dartLangComment;
-  final ModuleFormat moduleFormat;
-  final bool soundNullSafety;
-
-  SetupCompilerOptions(
-      {this.soundNullSafety = true, this.moduleFormat = ModuleFormat.amd})
-      : options = getOptions(soundNullSafety),
-        dartLangComment =
-            soundNullSafety ? dartSoundComment : dartUnsoundComment {
-    options.onDiagnostic = (DiagnosticMessage m) {
-      errors.addAll(m.plainTextFormatted);
-    };
-  }
-
-  String get loadModule {
-    switch (moduleFormat) {
-      case ModuleFormat.amd:
-        return 'require';
-      case ModuleFormat.ddc:
-        return 'dart_library.import';
-      default:
-        throw UnsupportedError('Module format: $moduleFormat');
-    }
-  }
-}
-
 /// Convenience class describing JavaScript module
 /// to ensure we have normalized module names
 class Module {
diff --git a/pkg/dev_compiler/test/module_symbols_test.dart b/pkg/dev_compiler/test/module_symbols/module_symbols_json_test.dart
similarity index 100%
rename from pkg/dev_compiler/test/module_symbols_test.dart
rename to pkg/dev_compiler/test/module_symbols/module_symbols_json_test.dart
diff --git a/pkg/dev_compiler/test/module_symbols/module_symbols_test.dart b/pkg/dev_compiler/test/module_symbols/module_symbols_test.dart
new file mode 100644
index 0000000..20fbfc1
--- /dev/null
+++ b/pkg/dev_compiler/test/module_symbols/module_symbols_test.dart
@@ -0,0 +1,245 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'dart:io' show Directory, File;
+
+import 'package:dev_compiler/dev_compiler.dart';
+import 'package:dev_compiler/src/kernel/command.dart';
+import 'package:dev_compiler/src/kernel/module_symbols.dart';
+import 'package:kernel/ast.dart' show Component, Library;
+import 'package:test/test.dart';
+
+import '../shared_test_options.dart';
+
+class TestCompiler {
+  final SetupCompilerOptions setup;
+
+  TestCompiler(this.setup);
+
+  Future<JSCode> compile({Uri input, Uri packages}) async {
+    // Initialize incremental compiler and create component.
+    setup.options.packagesFileUri = packages;
+    var compiler = DevelopmentIncrementalCompiler(setup.options, input);
+    var component = await compiler.computeDelta();
+    component.computeCanonicalNames();
+
+    // Initialize DDC.
+    var moduleName = 'foo.dart';
+    var classHierarchy = compiler.getClassHierarchy();
+    var compilerOptions = SharedCompilerOptions(
+        replCompile: true,
+        moduleName: moduleName,
+        soundNullSafety: setup.soundNullSafety,
+        moduleFormats: [setup.moduleFormat],
+        emitDebugSymbols: true);
+    var coreTypes = compiler.getCoreTypes();
+
+    final importToSummary = Map<Library, Component>.identity();
+    final summaryToModule = Map<Component, String>.identity();
+    for (var lib in component.libraries) {
+      importToSummary[lib] = component;
+    }
+    summaryToModule[component] = moduleName;
+
+    // Compile Kernel AST to JS AST.
+    var kernel2jsCompiler = ProgramCompiler(component, classHierarchy,
+        compilerOptions, importToSummary, summaryToModule,
+        coreTypes: coreTypes);
+    var moduleTree = kernel2jsCompiler.emitModule(component);
+
+    // Compile JS AST to code.
+    return jsProgramToCode(moduleTree, ModuleFormat.amd,
+        emitDebugSymbols: true,
+        compiler: kernel2jsCompiler,
+        component: component);
+  }
+}
+
+class TestDriver {
+  final SetupCompilerOptions options;
+  Directory tempDir;
+  final String source;
+  Uri input;
+  Uri packages;
+  File file;
+
+  TestDriver(this.options, this.source) {
+    var systemTempDir = Directory.systemTemp;
+    tempDir = systemTempDir.createTempSync('foo bar');
+
+    input = tempDir.uri.resolve('foo.dart');
+    file = File.fromUri(input)..createSync();
+    file.writeAsStringSync(source);
+
+    packages = tempDir.uri.resolve('package_config.json');
+    file = File.fromUri(packages)..createSync();
+    file.writeAsStringSync('''
+      {
+        "configVersion": 2,
+        "packages": [
+          {
+            "name": "foo",
+            "rootUri": "./",
+            "packageUri": "./"
+          }
+        ]
+      }
+      ''');
+  }
+
+  Future<JSCode> compile() async =>
+      await TestCompiler(options).compile(input: input, packages: packages);
+
+  void cleanUp() {
+    tempDir.delete(recursive: true);
+  }
+}
+
+class NullSafetyTestOption {
+  final String description;
+  final bool soundNullSafety;
+
+  NullSafetyTestOption(this.description, this.soundNullSafety);
+}
+
+void main() async {
+  for (var mode in [
+    NullSafetyTestOption('Sound Mode:', true),
+    NullSafetyTestOption('Weak Mode:', false)
+  ]) {
+    group(mode.description, () {
+      var options = SetupCompilerOptions(soundNullSafety: mode.soundNullSafety);
+      group('simple class debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {}
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('has name', () async {
+          expect(classSymbol.name, equals('A'));
+        });
+        test('is not abstract', () async {
+          expect(classSymbol.isAbstract, isFalse);
+        });
+        // TODO test isConst
+        test('has no superclassId', () async {
+          expect(classSymbol.superClassId, isNull);
+        });
+        test('empty interfacesIds', () async {
+          expect(classSymbol.interfaceIds, isEmpty);
+        });
+        test('empty typeParameters', () async {
+          expect(classSymbol.typeParameters, isEmpty);
+        });
+        test('has localId', () async {
+          expect(classSymbol.localId, equals('A'));
+        });
+        test('has library scopeId', () async {
+          expect(classSymbol.scopeId, endsWith('package:foo/foo.dart'));
+        });
+        group('location', () {
+          test('has scriptId', () async {
+            expect(classSymbol.location.scriptId, endsWith('/foo.dart'));
+          });
+          test('has start token', () async {
+            expect(classSymbol.location.tokenPos,
+                22 + options.dartLangComment.length);
+          });
+          test('has end token', () async {
+            expect(classSymbol.location.endTokenPos,
+                31 + options.dartLangComment.length);
+          });
+        });
+        test('no fields', () async {
+          expect(classSymbol.variableIds, isEmpty);
+        });
+        // TODO only has the implicit constructor in scopeIds.
+      });
+      group('abstract class debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          abstract class A {}
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('is abstract', () async {
+          expect(classSymbol.isAbstract, isTrue);
+        });
+      });
+      group('class extends debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A extends B {}
+
+          class B {}
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol =
+              result.symbols.classes.where((c) => c.localId == 'A').single;
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('has superclass', () async {
+          expect(classSymbol.superClassId, 'B');
+        });
+      });
+      group('class implements debug symbols', () {
+        TestDriver driver;
+        List<ClassSymbol> classSymbols;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A implements B, C {}
+
+          class B implements C {}
+
+          class C {}
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbols = result.symbols.classes;
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('single implements', () async {
+          var classSymbol = classSymbols.singleWhere((c) => c.localId == 'B');
+          expect(classSymbol.interfaceIds, orderedEquals(['C']));
+        });
+        test('multiple implements', () async {
+          var classSymbol = classSymbols.singleWhere((c) => c.localId == 'A');
+          expect(classSymbol.interfaceIds, orderedEquals(['B', 'C']));
+        });
+      });
+    });
+  }
+}
diff --git a/pkg/dev_compiler/test/shared_test_options.dart b/pkg/dev_compiler/test/shared_test_options.dart
new file mode 100644
index 0000000..53d8879
--- /dev/null
+++ b/pkg/dev_compiler/test/shared_test_options.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'package:cli_util/cli_util.dart';
+import 'package:dev_compiler/dev_compiler.dart';
+import 'package:dev_compiler/src/compiler/module_builder.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';
+import 'package:kernel/ast.dart' show Component;
+import 'package:kernel/target/targets.dart';
+import 'package:path/path.dart' as p;
+
+class DevelopmentIncrementalCompiler extends IncrementalCompiler {
+  Uri entryPoint;
+
+  DevelopmentIncrementalCompiler(CompilerOptions options, this.entryPoint,
+      [Uri initializeFrom,
+      bool outlineOnly,
+      IncrementalSerializer incrementalSerializer])
+      : super(
+            CompilerContext(
+                ProcessedOptions(options: options, inputs: [entryPoint])),
+            initializeFrom,
+            outlineOnly,
+            incrementalSerializer);
+
+  DevelopmentIncrementalCompiler.fromComponent(CompilerOptions options,
+      this.entryPoint, Component componentToInitializeFrom,
+      [bool outlineOnly, IncrementalSerializer incrementalSerializer])
+      : super.fromComponent(
+            CompilerContext(
+                ProcessedOptions(options: options, inputs: [entryPoint])),
+            componentToInitializeFrom,
+            outlineOnly,
+            incrementalSerializer);
+}
+
+class SetupCompilerOptions {
+  static final sdkRoot = computePlatformBinariesLocation();
+  static final sdkUnsoundSummaryPath = p.join(sdkRoot.path, 'ddc_sdk.dill');
+  static final sdkSoundSummaryPath =
+      p.join(sdkRoot.path, 'ddc_outline_sound.dill');
+  static final librariesSpecificationUri =
+      p.join(p.dirname(p.dirname(getSdkPath())), 'libraries.json');
+
+  static CompilerOptions getOptions(bool soundNullSafety) {
+    var options = CompilerOptions()
+      ..verbose = false // set to true for debugging
+      ..sdkRoot = sdkRoot
+      ..target = DevCompilerTarget(TargetFlags())
+      ..librariesSpecificationUri = Uri.base.resolve('sdk/lib/libraries.json')
+      ..omitPlatform = true
+      ..sdkSummary = sdkRoot.resolve(
+          soundNullSafety ? sdkSoundSummaryPath : sdkUnsoundSummaryPath)
+      ..environmentDefines = const {}
+      ..nnbdMode = soundNullSafety ? NnbdMode.Strong : NnbdMode.Weak;
+    return options;
+  }
+
+  static final String dartUnsoundComment = '// @dart = 2.9';
+  static final String dartSoundComment = '//';
+
+  final List<String> errors = [];
+  final CompilerOptions options;
+  final String dartLangComment;
+  final ModuleFormat moduleFormat;
+  final bool soundNullSafety;
+
+  SetupCompilerOptions(
+      {this.soundNullSafety = true, this.moduleFormat = ModuleFormat.amd})
+      : options = getOptions(soundNullSafety),
+        dartLangComment =
+            soundNullSafety ? dartSoundComment : dartUnsoundComment {
+    options.onDiagnostic = (DiagnosticMessage m) {
+      errors.addAll(m.plainTextFormatted);
+    };
+  }
+
+  String get loadModule {
+    switch (moduleFormat) {
+      case ModuleFormat.amd:
+        return 'require';
+      case ModuleFormat.ddc:
+        return 'dart_library.import';
+      default:
+        throw UnsupportedError('Module format: $moduleFormat');
+    }
+  }
+}
diff --git a/pkg/dev_compiler/tool/ddb b/pkg/dev_compiler/tool/ddb
index 56321d5..8c9efe1 100755
--- a/pkg/dev_compiler/tool/ddb
+++ b/pkg/dev_compiler/tool/ddb
@@ -50,6 +50,10 @@
         help: 'Compile for sound null safety at runtime. Passed through to the '
             'DDC binary. Defaults to false.',
         defaultsTo: false)
+    ..addFlag('emit-debug-symbols',
+        help: 'Pass through flag for DDC, emits debug symbols file along with '
+            'the compiled module.',
+        defaultsTo: false)
     ..addFlag('null-assertions',
         help: 'Run with assertions that values passed to non-nullable method '
             'parameters are not null.',
@@ -115,6 +119,7 @@
   var run = mode == 'run' || mode == 'all';
   var verbose = options['verbose'] as bool;
   var soundNullSafety = options['sound-null-safety'] as bool;
+  var emitDebugSymbols = options['emit-debug-symbols'] as bool;
   var nonNullAsserts = options['null-assertions'] as bool;
   var nativeNonNullAsserts = options['native-null-assertions'] as bool;
   var weakNullSafetyErrors = options['weak-null-safety-errors'] as bool;
@@ -230,6 +235,7 @@
       for (var experiment in experiments) '--enable-experiment=$experiment',
       if (soundNullSafety) '--sound-null-safety',
       if (options['packages'] != null) '--packages=${options['packages']}',
+      if (emitDebugSymbols) '--emit-debug-symbols',
       '-o',
       out,
       entry
diff --git a/pkg/test_runner/lib/src/command_output.dart b/pkg/test_runner/lib/src/command_output.dart
index 3e587e3..ae762aa 100644
--- a/pkg/test_runner/lib/src/command_output.dart
+++ b/pkg/test_runner/lib/src/command_output.dart
@@ -1463,6 +1463,8 @@
   }
 
   Expectation result(TestCase testCase) {
+    if (hasCrashed) return Expectation.crash;
+    if (hasTimedOut) return Expectation.timeout;
     if (hasNonUtf8) return Expectation.nonUtf8Error;
     if (truncatedOutput) return Expectation.truncatedOutput;
 
@@ -1475,6 +1477,8 @@
   }
 
   Expectation realResult(TestCase testCase) {
+    if (hasCrashed) return Expectation.crash;
+    if (hasTimedOut) return Expectation.timeout;
     if (hasNonUtf8) return Expectation.nonUtf8Error;
     if (truncatedOutput) return Expectation.truncatedOutput;
 
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 9dc7f89..fc091da 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -34,6 +34,9 @@
 [ $builder_tag == obfuscated ]
 no_such_method_error_with_invocation_test: SkipByDesign # Checks the member names in the NSM error message.
 
+[ $compiler == app_jitk ]
+io/socket_sigpipe_test: SkipByDesign # Spawns server process using Platform.executable
+
 [ $compiler == dart2analyzer ]
 deferred_transitive_import_error_test: Skip
 
@@ -94,6 +97,9 @@
 fragmentation_test: SkipSlow
 fragmentation_typed_data_test: SkipSlow
 
+[ $arch == simarm || $arch == simarm64 || $arch == simarm64c ]
+io/socket_sigpipe_test: SkipByDesign # Test uses ffi
+
 [ $compiler == dart2js || $compiler == dartdevc || $compiler == dartdevk ]
 *: SkipByDesign
 
diff --git a/tests/standalone_2/standalone_2.status b/tests/standalone_2/standalone_2.status
index 2ce913c..d875452 100644
--- a/tests/standalone_2/standalone_2.status
+++ b/tests/standalone_2/standalone_2.status
@@ -34,6 +34,9 @@
 [ $builder_tag == obfuscated ]
 no_such_method_error_with_invocation_test: SkipByDesign # Checks the member names in the NSM error message.
 
+[ $compiler == app_jitk ]
+io/socket_sigpipe_test: SkipByDesign # Spawns server process using Platform.executable
+
 [ $compiler == dart2analyzer ]
 deferred_transitive_import_error_test: Skip
 
@@ -101,6 +104,9 @@
 fragmentation_test: SkipSlow
 fragmentation_typed_data_test: SkipSlow
 
+[ $arch == simarm || $arch == simarm64 || $arch == simarm64c ]
+io/socket_sigpipe_test: SkipByDesign # Test uses ffi
+
 [ $compiler == dart2js || $compiler == dartdevc || $compiler == dartdevk ]
 *: SkipByDesign
 
diff --git a/tests/web/native/native_exceptions1_frog_test.dart b/tests/web/native/native_exceptions1_frog_test.dart
index 7585a39..5932196 100644
--- a/tests/web/native/native_exceptions1_frog_test.dart
+++ b/tests/web/native/native_exceptions1_frog_test.dart
@@ -44,8 +44,8 @@
   JS('', r"""
 (function(){
   // Ensure we are not relying on global names 'A' and 'E'.
-  A = null;
-  E = null;
+  self.A = null;
+  self.E = null;
 })()""");
   applyTestExtensions(['E', 'A']);
 }
@@ -53,6 +53,10 @@
 void setup2() {
   JS('', r"""
 (function(){
+// Confirm A and B are null.
+console.assert(self.A == null);
+console.assert(self.B == null);
+
 // This code is all inside 'setup2' and so not accessible from the global scope.
 function E(x){ this.code = x; }
 
diff --git a/tools/VERSION b/tools/VERSION
index f477762..b8cf35e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 220
+PRERELEASE 221
 PRERELEASE_PATCH 0
\ No newline at end of file