Version 1.13.1

Cherry-pick 3fa0cae192dce46aef1c9040e8782702aa1e2d9c to stable
Cherry-pick b837d63d47aed38ceae39b78ba4d04048475d0e5 to stable
Cherry-pick c315da5a06a6cbbe2b3424b5313254721cd40785 to stable
Cherry-pick dff13bef8de104d33b04820136da2d80f3c835d7 to stable
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
index fdcbd37..5d6d2b89 100644
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ b/pkg/compiler/lib/src/apiimpl.dart
@@ -69,6 +69,8 @@
                 hasOption(options, Flags.trustTypeAnnotations),
             trustPrimitives:
                 hasOption(options, Flags.trustPrimitives),
+            trustJSInteropTypeAnnotations:
+                hasOption(options, Flags.trustJSInteropTypeAnnotations),
             enableMinification: hasOption(options, Flags.minify),
             useFrequencyNamer:
                 !hasOption(options, Flags.noFrequencyBasedMinification),
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 668cb28..03f274e 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -40,6 +40,8 @@
   static const String testMode = '--test-mode';
   static const String trustPrimitives = '--trust-primitives';
   static const String trustTypeAnnotations = '--trust-type-annotations';
+  static const String trustJSInteropTypeAnnotations =
+      '--experimental-trust-js-interop-type-annotations';
   static const String useContentSecurityPolicy = '--csp';
   static const String useCpsIr = '--use-cps-ir';
   static const String verbose = '--verbose';
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 8fab748..2f980bb 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -181,6 +181,7 @@
   final bool enableUserAssertions;
   final bool trustTypeAnnotations;
   final bool trustPrimitives;
+  final bool trustJSInteropTypeAnnotations;
   final bool disableTypeInferenceFlag;
   final Uri deferredMapUri;
   final bool dumpInfo;
@@ -432,6 +433,7 @@
             this.enableUserAssertions: false,
             this.trustTypeAnnotations: false,
             this.trustPrimitives: false,
+            this.trustJSInteropTypeAnnotations: false,
             bool disableTypeInferenceFlag: false,
             this.maxConcreteTypeSize: 5,
             this.enableMinification: false,
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index fa7aeb7..662316b 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -119,6 +119,7 @@
   bool dumpInfo = false;
   bool allowNativeExtensions = false;
   bool trustTypeAnnotations = false;
+  bool trustJSInteropTypeAnnotations = false;
   bool checkedMode = false;
   // List of provided options that imply that output is expected.
   List<String> optionsImplyCompilation = <String>[];
@@ -227,6 +228,11 @@
     implyCompilation(argument);
   }
 
+  setTrustJSInteropTypeAnnotations(String argument) {
+    trustJSInteropTypeAnnotations = true;
+    implyCompilation(argument);
+  }
+
   setTrustPrimitives(String argument) {
     implyCompilation(argument);
   }
@@ -326,6 +332,9 @@
     new OptionHandler(Flags.trustPrimitives,
                       (_) => setTrustPrimitives(
                           Flags.trustPrimitives)),
+    new OptionHandler(Flags.trustJSInteropTypeAnnotations,
+                      (_) => setTrustJSInteropTypeAnnotations(
+                          Flags.trustJSInteropTypeAnnotations)),
     new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
     new OptionHandler('--packages=.+', setPackageConfig),
     new OptionHandler('--package-root=.+|-p.+', setPackageRoot),
diff --git a/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart
index e6cce5b..4199c69 100644
--- a/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart
@@ -116,6 +116,10 @@
 
       ClassElement classElement = element;
 
+      // Skip classes that are completely unreachable. This should only happen
+      // when all of jsinterop types are unreachable from main.
+      if (!backend.compiler.world.isImplemented(classElement)) return;
+
       if (!classElement
           .implementsInterface(backend.jsJavaScriptObjectClass)) {
         backend.reporter.reportErrorMessage(classElement,
diff --git a/pkg/compiler/lib/src/native/behavior.dart b/pkg/compiler/lib/src/native/behavior.dart
index 7dbab30..63d5bd6 100644
--- a/pkg/compiler/lib/src/native/behavior.dart
+++ b/pkg/compiler/lib/src/native/behavior.dart
@@ -645,12 +645,26 @@
   static NativeBehavior ofMethod(FunctionElement method,  Compiler compiler) {
     FunctionType type = method.computeType(compiler.resolution);
     var behavior = new NativeBehavior();
-    behavior.typesReturned.add(type.returnType);
+    var returnType = type.returnType;
+    bool isInterop = method.isJsInterop;
+    // Note: For dart:html and other internal libraries we maintain, we can
+    // trust the return type and use it to limit what we enqueue. We have to
+    // be more conservative about JS interop types and assume they can return
+    // anything (unless the user provides the experimental flag to trust the
+    // type of js-interop APIs). We do restrict the allocation effects and say
+    // that interop calls create only interop types (which may be unsound if
+    // an interop call returns a DOM type and declares a dynamic return type,
+    // but otherwise we would include a lot of code by default).
+    // TODO(sigmund,sra): consider doing something better for numeric types.
+    behavior.typesReturned.add(
+        !isInterop || compiler.trustJSInteropTypeAnnotations ? returnType
+        : const DynamicType());
     if (!type.returnType.isVoid) {
       // Declared types are nullable.
       behavior.typesReturned.add(compiler.coreTypes.nullType);
     }
-    behavior._capture(type, compiler.resolution);
+    behavior._capture(type, compiler.resolution,
+        isInterop: isInterop, compiler: compiler);
 
     // TODO(sra): Optional arguments are currently missing from the
     // DartType. This should be fixed so the following work-around can be
@@ -668,10 +682,15 @@
     Resolution resolution = compiler.resolution;
     DartType type = field.computeType(resolution);
     var behavior = new NativeBehavior();
-    behavior.typesReturned.add(type);
+    bool isInterop = field.isJsInterop;
+    // TODO(sigmund,sra): consider doing something better for numeric types.
+    behavior.typesReturned.add(
+        !isInterop || compiler.trustJSInteropTypeAnnotations ? type
+        : const DynamicType());
     // Declared types are nullable.
     behavior.typesReturned.add(resolution.coreTypes.nullType);
-    behavior._capture(type, resolution);
+    behavior._capture(type, resolution,
+        isInterop: isInterop, compiler: compiler);
     behavior._overrideWithAnnotations(field, compiler);
     return behavior;
   }
@@ -765,17 +784,50 @@
   /// Models the behavior of Dart code receiving instances and methods of [type]
   /// from native code.  We usually start the analysis by capturing a native
   /// method that has been used.
-  void _capture(DartType type, Resolution resolution) {
+  ///
+  /// We assume that JS-interop APIs cannot instantiate Dart types or
+  /// non-JSInterop native types.
+  void _capture(DartType type, Resolution resolution,
+      {bool isInterop: false, Compiler compiler}) {
     type.computeUnaliased(resolution);
     type = type.unaliased;
     if (type is FunctionType) {
       FunctionType functionType = type;
-      _capture(functionType.returnType, resolution);
+      _capture(functionType.returnType, resolution,
+          isInterop: isInterop, compiler: compiler);
       for (DartType parameter in functionType.parameterTypes) {
         _escape(parameter, resolution);
       }
     } else {
-      typesInstantiated.add(type);
+      DartType instantiated = null;
+      JavaScriptBackend backend = compiler?.backend;
+      if (!isInterop) {
+        typesInstantiated.add(type);
+      } else {
+        if (type.element != null && type.element.isNative) {
+          // Any declared native or interop type (isNative implies isJsInterop)
+          // is assumed to be allocated.
+          typesInstantiated.add(type);
+        }
+
+        if (!compiler.trustJSInteropTypeAnnotations ||
+          type.isDynamic || type.isObject) {
+          // By saying that only JS-interop types can be created, we prevent
+          // pulling in every other native type (e.g. all of dart:html) when a
+          // JS interop API returns dynamic or when we don't trust the type
+          // annotations. This means that to some degree we still use the return
+          // type to decide whether to include native types, even if we don't
+          // trust the type annotation.
+          ClassElement cls = backend.jsJavaScriptObjectClass;
+          cls.ensureResolved(resolution);
+          typesInstantiated.add(cls.thisType);
+        } else {
+          // Otherwise, when the declared type is a Dart type, we do not
+          // register an allocation because we assume it cannot be instantiated
+          // from within the JS-interop code. It must have escaped from another
+          // API.
+        }
+      }
     }
   }
 
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 0258d7d..a913c77 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -5852,7 +5852,7 @@
         && params.optionalParametersAreNamed;
   }
 
-  HForeignCode invokeJsInteropFunction(Element element,
+  HForeignCode invokeJsInteropFunction(FunctionElement element,
                                        List<HInstruction> arguments,
                                        SourceInformation sourceInformation) {
     assert(element.isJsInterop);
@@ -5886,6 +5886,9 @@
 
       var nativeBehavior = new native.NativeBehavior()
         ..codeTemplate = codeTemplate;
+      if (compiler.trustJSInteropTypeAnnotations) {
+        nativeBehavior.typesReturned.add(constructor.enclosingClass.thisType);
+      }
       return new HForeignCode(
           codeTemplate,
           backend.dynamicType, filteredArguments,
@@ -5905,34 +5908,43 @@
     arguments = arguments.where((arg) => arg != null).toList();
     var inputs = <HInstruction>[target]..addAll(arguments);
 
-    js.Template codeTemplate;
-    if (element.isGetter) {
-      codeTemplate = js.js.parseForeignJS("#");
-    } else if (element.isSetter) {
-      codeTemplate = js.js.parseForeignJS("# = #");
-    } else {
-      FunctionElement function = element;
-      FunctionSignature params = function.functionSignature;
+    var nativeBehavior = new native.NativeBehavior()
+      ..sideEffects.setAllSideEffects();
 
-      var argsStub = <String>[];
-      for (int i = 0; i < arguments.length; i++) {
-        argsStub.add('#');
-      }
+    DartType type = element.isConstructor ?
+        element.enclosingClass.thisType : element.type.returnType;
+    // Native behavior effects here are similar to native/behavior.dart.
+    // The return type is dynamic if we don't trust js-interop type
+    // declarations.
+    nativeBehavior.typesReturned.add(
+        compiler.trustJSInteropTypeAnnotations ? type : const DynamicType());
 
-      if (element.isConstructor) {
-        codeTemplate = js.js.parseForeignJS("new #(${argsStub.join(",")})");
-      } else {
-        codeTemplate = js.js.parseForeignJS("#(${argsStub.join(",")})");
-      }
+    // The allocation effects include the declared type if it is native (which
+    // includes js interop types).
+    if (type.element != null && type.element.isNative) {
+      nativeBehavior.typesInstantiated.add(type);
     }
 
-    var nativeBehavior = new native.NativeBehavior()
-      ..codeTemplate = codeTemplate
-      ..typesReturned.add(
-          backend.jsJavaScriptObjectClass.thisType)
-      ..typesInstantiated.add(
-          backend.jsJavaScriptObjectClass.thisType)
-      ..sideEffects.setAllSideEffects();
+    // It also includes any other JS interop type if we don't trust the
+    // annotation or if is declared too broad.
+    if (!compiler.trustJSInteropTypeAnnotations || type.isObject ||
+        type.isDynamic) {
+      nativeBehavior.typesInstantiated.add(
+          backend.jsJavaScriptObjectClass.thisType);
+    }
+
+    String code;
+    if (element.isGetter) {
+      code = "#";
+    } else if (element.isSetter) {
+      code = "# = #";
+    } else {
+      var args = new List.filled(arguments.length, '#').join(',');
+      code = element.isConstructor ? "new #($args)" : "#($args)";
+    }
+    js.Template codeTemplate = js.js.parseForeignJS(code);
+    nativeBehavior.codeTemplate = codeTemplate;
+
     return new HForeignCode(
         codeTemplate,
         backend.dynamicType, inputs,
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 815b796..12fb7ff 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -104,6 +104,8 @@
                           Address ad) {
   ASSERT(rd != kNoRegister);
   ASSERT(cond != kNoCondition);
+  ASSERT(!ad.has_writeback() || (ad.rn() != rd));  // Unpredictable.
+
   int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
                      B26 | (ad.kind() == Address::Immediate ? 0 : B25) |
                      (load ? L : 0) |
diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h
index 9ba279c..78fb22d 100644
--- a/runtime/vm/assembler_arm.h
+++ b/runtime/vm/assembler_arm.h
@@ -321,6 +321,11 @@
 
   Mode mode() const { return static_cast<Mode>(encoding() & kModeMask); }
 
+  bool has_writeback() const {
+    return (mode() == PreIndex) || (mode() == PostIndex) ||
+           (mode() == NegPreIndex) || (mode() == NegPostIndex);
+  }
+
   uint32_t encoding() const { return encoding_; }
 
   // Encoding for addressing mode 3.
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 3448fd0..29ec735 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -149,8 +149,6 @@
     }
   }
 
-  HandleBranchOnStrictCompare(block);
-
   for (intptr_t i = 0; i < block->dominated_blocks().length(); ++i) {
     PropagateRecursive(block->dominated_blocks()[i]);
   }
@@ -159,61 +157,6 @@
 }
 
 
-void FlowGraphTypePropagator::HandleBranchOnStrictCompare(
-    BlockEntryInstr* block) {
-  BranchInstr* branch = block->last_instruction()->AsBranch();
-  if (branch == NULL) {
-    return;
-  }
-
-  StrictCompareInstr* compare = branch->comparison()->AsStrictCompare();
-  if ((compare == NULL) || !compare->right()->BindsToConstant()) {
-    return;
-  }
-
-  const intptr_t rollback_point = rollback_.length();
-
-  Definition* defn = compare->left()->definition();
-  const Object& right = compare->right()->BoundConstant();
-  intptr_t cid = right.GetClassId();
-
-  if (defn->IsLoadClassId() && right.IsSmi()) {
-    defn = defn->AsLoadClassId()->object()->definition();
-    cid = Smi::Cast(right).Value();
-  }
-
-  if (!CheckClassInstr::IsImmutableClassId(cid)) {
-    if ((cid == kOneByteStringCid) || (cid == kTwoByteStringCid)) {
-      SetTypeOf(defn, ZoneCompileType::Wrap(CompileType::String()));
-      PropagateRecursive((compare->kind() == Token::kEQ_STRICT) ?
-          branch->true_successor() : branch->false_successor());
-      RollbackTo(rollback_point);
-    }
-    return;
-  }
-
-  if (compare->kind() == Token::kEQ_STRICT) {
-    if (cid == kNullCid) {
-      branch->set_constrained_type(MarkNonNullable(defn));
-      PropagateRecursive(branch->false_successor());
-    }
-
-    SetCid(defn, cid);
-    PropagateRecursive(branch->true_successor());
-  } else if (compare->kind() == Token::kNE_STRICT) {
-    if (cid == kNullCid) {
-      branch->set_constrained_type(MarkNonNullable(defn));
-      PropagateRecursive(branch->true_successor());
-    }
-
-    SetCid(defn, cid);
-    PropagateRecursive(branch->false_successor());
-  }
-
-  RollbackTo(rollback_point);
-}
-
-
 void FlowGraphTypePropagator::RollbackTo(intptr_t rollback_point) {
   for (intptr_t i = rollback_.length() - 1; i >= rollback_point; i--) {
     types_[rollback_[i].index()] = rollback_[i].type();
diff --git a/runtime/vm/flow_graph_type_propagator.h b/runtime/vm/flow_graph_type_propagator.h
index 4b1baf4..3c10917 100644
--- a/runtime/vm/flow_graph_type_propagator.h
+++ b/runtime/vm/flow_graph_type_propagator.h
@@ -20,7 +20,6 @@
   void Propagate();
 
   void PropagateRecursive(BlockEntryInstr* block);
-  void HandleBranchOnStrictCompare(BlockEntryInstr* block);
 
   void RollbackTo(intptr_t rollback_point);
 
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index 5419aaa..531ed4a 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -1996,6 +1996,7 @@
         HandleIllegalAccess(addr, instr);
       } else {
         if (write_back) {
+          ASSERT(rd != rn);  // Unpredictable.
           set_register(rn, rn_val);
         }
         if (!instr->HasSign()) {
@@ -2312,6 +2313,7 @@
     HandleIllegalAccess(addr, instr);
   } else {
     if (write_back) {
+      ASSERT(rd != rn);  // Unpredictable.
       set_register(rn, rn_val);
     }
     if (instr->HasB()) {
@@ -2424,6 +2426,7 @@
     HandleIllegalAccess(addr, instr);
   } else {
     if (write_back) {
+      ASSERT(rd != rn);  // Unpredictable.
       set_register(rn, rn_val);
     }
     if (instr->HasB()) {
diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc
index 5acd669..1629107 100644
--- a/runtime/vm/stub_code_arm.cc
+++ b/runtime/vm/stub_code_arm.cc
@@ -419,6 +419,9 @@
   __ eor(IP, IP, Operand(LR));
 
   // Set up the frame manually with return address now stored in IP.
+  COMPILE_ASSERT(PP < CODE_REG);
+  COMPILE_ASSERT(CODE_REG < FP);
+  COMPILE_ASSERT(FP < IP);
   __ EnterFrame((1 << PP) | (1 << CODE_REG) | (1 << FP) | (1 << IP), 0);
   __ LoadPoolPointer();
 
@@ -434,9 +437,12 @@
     if (i == CODE_REG) {
       // Save the original value of CODE_REG pushed before invoking this stub
       // instead of the value used to call this stub.
-      COMPILE_ASSERT(IP > CODE_REG);  // Assert IP is pushed first.
       __ ldr(IP, Address(FP, kCallerSpSlotFromFp * kWordSize));
       __ Push(IP);
+    } else if (i == SP) {
+      // Push(SP) has unpredictable behavior.
+      __ mov(IP, Operand(SP));
+      __ Push(IP);
     } else {
       __ Push(static_cast<Register>(i));
     }
diff --git a/tests/compiler/dart2js/compiler_helper.dart b/tests/compiler/dart2js/compiler_helper.dart
index d3ac2ac..2604401 100644
--- a/tests/compiler/dart2js/compiler_helper.dart
+++ b/tests/compiler/dart2js/compiler_helper.dart
@@ -43,7 +43,8 @@
 import 'output_collector.dart';
 export 'output_collector.dart';
 
-/// Compile [code] and returns the code for [entry].
+/// Compile [code] and returns either the code for [entry] or, if [returnAll] is
+/// true, the code for the entire program.
 ///
 /// If [check] is provided, it is executed on the code for [entry] before
 /// returning. If [useMock] is `true` the [MockCompiler] is used for
@@ -54,8 +55,11 @@
                         bool minify: false,
                         bool analyzeAll: false,
                         bool disableInlining: true,
+                        bool trustJSInteropTypeAnnotations: false,
                         bool useMock: false,
-                        void check(String generated)}) async {
+                        void check(String generatedEntry),
+                        bool returnAll: false}) async {
+  OutputCollector outputCollector = returnAll ? new OutputCollector() : null;
   if (useMock) {
     // TODO(johnniwinther): Remove this when no longer needed by
     // `arithmetic_simplication_test.dart`.
@@ -65,7 +69,9 @@
         // compiling a method.
         disableTypeInference: true,
         enableMinification: minify,
-        disableInlining: disableInlining);
+        disableInlining: disableInlining,
+        trustJSInteropTypeAnnotations: trustJSInteropTypeAnnotations,
+        outputProvider: outputCollector);
     await compiler.init();
     compiler.parseScript(code);
     lego.Element element = compiler.mainApp.find(entry);
@@ -89,7 +95,7 @@
     if (check != null) {
       check(generated);
     }
-    return generated;
+    return returnAll ? outputCollector.getOutput('', 'js') : generated;
   } else {
     List<String> options = <String>[
         Flags.disableTypeInference];
@@ -102,6 +108,9 @@
     if (analyzeAll) {
       options.add(Flags.analyzeAll);
     }
+    if (trustJSInteropTypeAnnotations) {
+      options.add(Flags.trustJSInteropTypeAnnotations);
+    }
 
     Map<String, String> source;
     if (entry != 'main') {
@@ -113,6 +122,7 @@
     CompilationResult result = await runCompiler(
         memorySourceFiles: source,
         options: options,
+        outputProvider: outputCollector,
         beforeRun: (compiler) {
           if (disableInlining) {
             compiler.disableInlining = true;
@@ -126,7 +136,7 @@
     if (check != null) {
       check(generated);
     }
-    return generated;
+    return returnAll ? outputCollector.getOutput('', 'js') : generated;
   }
 }
 
diff --git a/tests/compiler/dart2js/interop_anonymous_unreachable_test.dart b/tests/compiler/dart2js/interop_anonymous_unreachable_test.dart
new file mode 100644
index 0000000..3f38205
--- /dev/null
+++ b/tests/compiler/dart2js/interop_anonymous_unreachable_test.dart
@@ -0,0 +1,177 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library tests.dart2js.interop_anonymous_unreachable_test;
+
+import 'dart:async';
+
+import 'package:test/test.dart';
+import 'compiler_helper.dart';
+
+
+main() {
+  test("unreachable code doesn't crash the compiler", () async {
+    // This test is a regression for Issue #24974
+    String generated = await compile("""
+        import 'package:js/js.dart';
+
+        @JS() @anonymous
+        class UniqueLongNameForTesting_A {
+          external factory UniqueLongNameForTesting_A();
+        }
+        main() {}
+    """, returnAll: true);
+
+    // the code should not be included in the output either.
+    expect(generated, isNot(contains("UniqueLongNameForTesting_A")));
+  });
+
+  group('tree-shaking interop types', () {
+    String program = """
+        import 'package:js/js.dart';
+
+        // reachable and allocated
+        @JS() @anonymous
+        class UniqueLongNameForTesting_A {
+          external bool get x;
+          external UniqueLongNameForTesting_D get d;
+          external UniqueLongNameForTesting_E get e;
+          external factory UniqueLongNameForTesting_A(
+              {UniqueLongNameForTesting_B arg0});
+        }
+
+        // visible through the parameter above, but not used.
+        @JS() @anonymous
+        class UniqueLongNameForTesting_B {
+          external factory UniqueLongNameForTesting_B();
+        }
+
+        // unreachable
+        @JS() @anonymous
+        class UniqueLongNameForTesting_C {
+          external factory UniqueLongNameForTesting_C();
+        }
+
+        // visible and reached through `d`.
+        @JS() @anonymous
+        class UniqueLongNameForTesting_D {
+          external factory UniqueLongNameForTesting_D();
+        }
+
+        // visible through `e`, but not reached.
+        @JS() @anonymous
+        class UniqueLongNameForTesting_E {
+          external factory UniqueLongNameForTesting_E();
+        }
+
+        main() {
+          print(new UniqueLongNameForTesting_A().x);
+          print(new UniqueLongNameForTesting_A().d);
+        }
+    """;
+
+    test('no tree-shaking by default', () async {
+      String generated = await compile(program, returnAll: true);
+      expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+      expect(generated.contains("UniqueLongNameForTesting_D"), isTrue);
+
+      expect(generated.contains("UniqueLongNameForTesting_B"), isTrue);
+      expect(generated.contains("UniqueLongNameForTesting_C"), isTrue);
+      expect(generated.contains("UniqueLongNameForTesting_E"), isTrue);
+    });
+
+    test('tree-shake when using flag', () async {
+      String generated = await compile(program,
+          trustJSInteropTypeAnnotations: true,
+          returnAll: true);
+      expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+      expect(generated.contains("UniqueLongNameForTesting_D"), isTrue);
+
+      expect(generated.contains("UniqueLongNameForTesting_B"), isFalse);
+      expect(generated.contains("UniqueLongNameForTesting_C"), isFalse);
+      expect(generated.contains("UniqueLongNameForTesting_E"), isFalse);
+    });
+  });
+
+  group('tree-shaking other native types', () {
+    String program = """
+        import 'dart:html';
+        import 'package:js/js.dart';
+
+        @JS() @anonymous
+        class UniqueLongNameForTesting_A {
+          external dynamic get x;
+        }
+
+        @JS() @anonymous
+        class UniqueLongNameForTesting_B {
+          external dynamic get y;
+        }
+
+        main() {
+          print(new UniqueLongNameForTesting_A().x);
+        }
+    """;
+
+    test('allocation effect of dynamic excludes native types', () async {
+      String generated = await compile(program, returnAll: true);
+      expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+      // any js-interop type could be allocated by `get x`
+      expect(generated.contains("UniqueLongNameForTesting_B"), isTrue);
+      // but we exclude other native types like HTMLAudioElement
+      expect(generated.contains("HTMLAudioElement"), isFalse);
+    });
+
+    test('allocation effect of dynamic excludes native types [flag]', () async {
+      // Trusting types doesn't make a difference.
+      String generated = await compile(program,
+          trustJSInteropTypeAnnotations: true,
+          returnAll: true);
+      expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+      expect(generated.contains("UniqueLongNameForTesting_B"), isTrue);
+      expect(generated.contains("HTMLAudioElement"), isFalse);
+    });
+
+    test('declared native types are included in allocation effect', () async {
+      String program2 = """
+          import 'dart:html';
+          import 'package:js/js.dart';
+
+          @JS() @anonymous
+          class UniqueLongNameForTesting_A {
+            external AudioElement get x;
+          }
+
+          main() {
+            print(new UniqueLongNameForTesting_A().x is AudioElement);
+          }
+      """;
+
+      String generated = await compile(program2, returnAll: true);
+      expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+      expect(generated.contains("HTMLAudioElement"), isTrue);
+
+      program2 = """
+          import 'dart:html';
+          import 'package:js/js.dart';
+
+          @JS() @anonymous
+          class UniqueLongNameForTesting_A {
+            external dynamic get x;
+          }
+
+          main() {
+            print(new UniqueLongNameForTesting_A().x is AudioElement);
+          }
+      """;
+
+      generated = await compile(program2, returnAll: true);
+      expect(generated.contains("UniqueLongNameForTesting_A"), isTrue);
+      // This extra check is to make sure that we don't include HTMLAudioElement
+      // just because of the is-check. It is optimized away in this case because
+      // we believe it was never instantiated.
+      expect(generated.contains("HTMLAudioElement"), isFalse);
+    });
+  });
+}
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index 7456c29..2195fe6 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -87,6 +87,7 @@
        // affected by inlining support.
        bool disableInlining: true,
        bool trustTypeAnnotations: false,
+       bool trustJSInteropTypeAnnotations: false,
        bool enableAsyncAwait: false,
        int this.expectedWarnings,
        int this.expectedErrors,
@@ -106,6 +107,7 @@
               emitJavaScript: emitJavaScript,
               preserveComments: preserveComments,
               trustTypeAnnotations: trustTypeAnnotations,
+              trustJSInteropTypeAnnotations: trustJSInteropTypeAnnotations,
               diagnosticOptions:
                   new DiagnosticOptions(showPackageWarnings: true),
               outputProvider: new LegacyCompilerOutput(outputProvider)) {
diff --git a/tests/html/html.status b/tests/html/html.status
index 56276ae..bba61dd 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -9,8 +9,8 @@
 [ $compiler == none && ($runtime == dartium || $runtime == drt) ]
 
 js_array_test: Skip # Dartium JSInterop failure
-js_typed_interop_test: Skip # Dartium JSInterop failure
-mirrors_js_typed_interop_test: Skip # Dartium JSInterop failure
+mirrors_js_typed_interop_test: Fail # Missing expected failure (Issue 25044)
+js_typed_interop_side_cast_exp_test: Fail, OK # tests dart2js-specific behavior.
 
 cross_domain_iframe_test: RuntimeError # Dartium JSInterop failure
 indexeddb_2_test: Fail # Dartium JSInterop failure. Identity preservation on array deferred copy.
@@ -19,7 +19,6 @@
 native_gc_test: Skip # Dartium JSInterop failure
 transferables_test: RuntimeError # Dartium JSInterop failure
 
-
 [ $compiler == none && ($runtime == drt || $runtime == dartium ) ]
 worker_api_test: Fail # Issue 10223
 resource_http_test: Fail # Issue 24203
diff --git a/tests/html/js_dart_to_string_test.dart b/tests/html/js_dart_to_string_test.dart
index 5293053..d2c23f4 100644
--- a/tests/html/js_dart_to_string_test.dart
+++ b/tests/html/js_dart_to_string_test.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 @JS()
-library js_typed_interop_test;
+library js_dart_to_string_test;
 
 import 'dart:html';
 
diff --git a/tests/html/js_typed_interop_anonymous2_exp_test.dart b/tests/html/js_typed_interop_anonymous2_exp_test.dart
new file mode 100644
index 0000000..baebd03
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous2_exp_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Same test as js_typed_interop_anonymous2, but using the
+// --experimental-trust-js-interop-type-annotations flag.
+library js_typed_interop_anonymous2_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+  external factory A({B b});
+
+  external B get b;
+}
+
+@JS() @anonymous
+class B {
+  external factory B({C c});
+
+  external C get c;
+}
+
+@JS() @anonymous
+class C {
+  external factory C();
+}
+
+// D is unreachable, and that is OK
+@JS() @anonymous
+class D {
+  external factory D();
+}
+
+main() {
+  useHtmlConfiguration();
+
+  test('simple', () {
+    var b = new B();
+    var a = new A(b: b);
+    expect(a.b, equals(b));
+    expect(b.c, isNull);
+  });
+}
diff --git a/tests/html/js_typed_interop_anonymous2_test.dart b/tests/html/js_typed_interop_anonymous2_test.dart
new file mode 100644
index 0000000..ed0e732
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous2_test.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library js_typed_interop_anonymous2_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+  external factory A({B b});
+
+  external B get b;
+}
+
+@JS() @anonymous
+class B {
+  external factory B({C c});
+
+  external C get c;
+}
+
+@JS() @anonymous
+class C {
+  external factory C();
+}
+
+// D is unreachable, and that is OK
+@JS() @anonymous
+class D {
+  external factory D();
+}
+
+main() {
+  useHtmlConfiguration();
+
+  test('simple', () {
+    var b = new B();
+    var a = new A(b: b);
+    expect(a.b, equals(b));
+    expect(b.c, isNull);
+  });
+}
diff --git a/tests/html/js_typed_interop_anonymous_exp_test.dart b/tests/html/js_typed_interop_anonymous_exp_test.dart
new file mode 100644
index 0000000..40a04aa
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_exp_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Same test as js_typed_interop_anonymous, but using the
+// --experimental-trust-js-interop-type-annotations flag.
+library js_typed_interop_anonymous_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+  external factory Literal({int x, String y, num z});
+
+  external int get x;
+  external String get y;
+  external num get z;
+}
+
+main() {
+  useHtmlConfiguration();
+
+  test('simple', () {
+    var l = new Literal(x: 3, y: "foo");
+    expect(l.x, equals(3));
+    expect(l.y, equals("foo"));
+    expect(l.z, isNull);
+  });
+}
diff --git a/tests/html/js_typed_interop_anonymous_test.dart b/tests/html/js_typed_interop_anonymous_test.dart
new file mode 100644
index 0000000..b2d1593
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library js_typed_interop_anonymous_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+  external factory Literal({int x, String y, num z});
+
+  external int get x;
+  external String get y;
+  external num get z;
+}
+
+main() {
+  useHtmlConfiguration();
+
+  test('simple', () {
+    var l = new Literal(x: 3, y: "foo");
+    expect(l.x, equals(3));
+    expect(l.y, equals("foo"));
+    expect(l.z, isNull);
+  });
+}
diff --git a/tests/html/js_typed_interop_anonymous_unreachable_exp_test.dart b/tests/html/js_typed_interop_anonymous_unreachable_exp_test.dart
new file mode 100644
index 0000000..a4e2c96
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_unreachable_exp_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Same test as js_typed_interop_anonymous_unreachable, but using the
+// --experimental-trust-js-interop-type-annotations flag.
+library js_typed_interop_anonymous_unreachable_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+  external factory Literal({int x, String y, num z});
+
+  external int get x;
+  external String get y;
+  external num get z;
+}
+
+main() {
+  useHtmlConfiguration();
+  test('nothing to do', () {
+    // This test is empty, but it is a regression for Issue# 24974: dartjs
+    // would crash trying to compile code that used @anonymous and that was
+    // not reachable from main.
+  });
+}
diff --git a/tests/html/js_typed_interop_anonymous_unreachable_test.dart b/tests/html/js_typed_interop_anonymous_unreachable_test.dart
new file mode 100644
index 0000000..ba8758e
--- /dev/null
+++ b/tests/html/js_typed_interop_anonymous_unreachable_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library js_typed_interop_anonymous_unreachable_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class Literal {
+  external factory Literal({int x, String y, num z});
+
+  external int get x;
+  external String get y;
+  external num get z;
+}
+
+main() {
+  useHtmlConfiguration();
+  test('nothing to do', () {
+    // This test is empty, but it is a regression for Issue# 24974: dartjs
+    // would crash trying to compile code that used @anonymous and that was
+    // not reachable from main.
+  });
+}
diff --git a/tests/html/js_typed_interop_side_cast_exp_test.dart b/tests/html/js_typed_interop_side_cast_exp_test.dart
new file mode 100644
index 0000000..6fd929a
--- /dev/null
+++ b/tests/html/js_typed_interop_side_cast_exp_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedOptions=--experimental-trust-js-interop-type-annotations
+
+// Similar test to js_typed_interop_side_cast, but because we are using the
+// --experimental-trust-js-interop-type-annotations flag, we test a slighly
+// different behavior.
+library js_typed_interop_side_cast_exp_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+  external int get x;
+  external factory A({int x});
+}
+
+@JS() @anonymous
+class B {
+  external int get x;
+  external factory B({int x});
+}
+
+@JS() @anonymous
+class C {
+  external int get x;
+  external factory C({int x});
+}
+
+main() {
+  useHtmlConfiguration();
+
+  test('side-casts work for reachable types', () {
+    new C(x: 3); // make C reachable
+    var a = new A(x: 3);
+    expect(a is C, isTrue);
+    C c = a;
+    expect(c.x, equals(3));
+  });
+
+  // Note: this test would fail without the experimental flag.
+  test('side-casts do not work for unreachable types', () {
+    var a = new A(x: 3);
+    expect(a is B, isFalse);
+  });
+}
diff --git a/tests/html/js_typed_interop_side_cast_test.dart b/tests/html/js_typed_interop_side_cast_test.dart
new file mode 100644
index 0000000..d50e1ac
--- /dev/null
+++ b/tests/html/js_typed_interop_side_cast_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library js_typed_interop_anonymous2_test;
+
+import 'dart:html';
+import 'dart:js' as js;
+
+import 'package:js/js.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+@JS() @anonymous
+class A {
+  external int get x;
+  external factory A({int x});
+}
+
+@JS() @anonymous
+class C {
+  external int get x;
+  external factory C({int x});
+}
+
+@JS() @anonymous
+class B {
+  external int get x;
+  external factory B({int x});
+}
+
+main() {
+  useHtmlConfiguration();
+
+  test('side-casts work for reachable types', () {
+    new C(x: 3); // make C reachable
+    var a = new A(x: 3);
+    expect(a is C, isTrue);
+    C c = a;
+    expect(c.x, equals(3));
+  });
+
+  test('side-casts work for otherwise unreachable types', () {
+    var a = new A(x: 3);
+    expect(a is B, isTrue);
+  });
+}
diff --git a/tests/language/vm/type_propagation_test.dart b/tests/language/vm/type_propagation_test.dart
new file mode 100644
index 0000000..935c096
--- /dev/null
+++ b/tests/language/vm/type_propagation_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// VMOptions=--optimization-counter-threshold=1000 --max-polymorphic-checks=1
+
+// Test correct loop invariant code motion and type propagation from is-checks
+// and null-comparisons. 
+
+class B {
+  var b;
+  B(this.b);
+}
+
+class C {
+  final f0;
+
+  final a;
+  C() : a = new B(0);
+}
+
+foo(x) {
+  for (var i = 0; i < 10; i++) {
+    i + i;
+    i + i;
+    if (x is C) {
+      x.a.b < 0;
+    }
+  }
+}
+
+class Y { var f = null; }
+
+bar(y) {
+  var x = y.f;
+  for (var i = 0; i < 10; i++) {
+    if (x != null) {
+      x.a.b < 0;
+    }
+  }
+}
+
+
+main () {
+  var o = new Y();
+  o.f = new C();
+  bar(o);
+  o.f = null;
+  bar(o);
+
+  for (var i = 0; i < 1000; i++) bar(o);
+
+  foo(new C());
+  foo(0);
+
+  for (var i = 0; i < 1000; i++) foo(0);
+}
diff --git a/tools/VERSION b/tools/VERSION
index b920ee5..73cc853 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
 CHANNEL stable
 MAJOR 1
 MINOR 13
-PATCH 0
+PATCH 1
 PRERELEASE 0
 PRERELEASE_PATCH 0