Version 2.15.0-156.0.dev

Merge commit '091338d7a3359f60d0acff7b9972c839b90600ed' into 'dev'
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 2725ad3..3f0ddf0 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -52,8 +52,9 @@
     to[key] = from[key];
   }
 }
+
 // Copies the own properties from [from] to [to] if not already present in [to].
-function mixinProperties(from, to) {
+function mixinPropertiesHard(from, to) {
   var keys = Object.keys(from);
   for (var i = 0; i < keys.length; i++) {
     var key = keys[i];
@@ -62,6 +63,15 @@
     }
   }
 }
+// Copies the own properties from [from] to [to] (specialized version of
+// `mixinPropertiesHard` when it is known the properties are disjoint).
+function mixinPropertiesEasy(from, to) {
+  if (#legacyJavaScript) {
+    copyProperties(from, to);
+  } else {
+    Object.assign(to, from);
+  }
+}
 
 // Only use direct proto access to construct the prototype chain (instead of
 // copying properties) on platforms where we know it works well (Chrome / d8).
@@ -117,8 +127,12 @@
 }
 
 // Mixes in the properties of [mixin] into [cls].
-function mixin(cls, mixin) {
-  mixinProperties(mixin.prototype, cls.prototype);
+function mixinEasy(cls, mixin) {
+  mixinPropertiesEasy(mixin.prototype, cls.prototype);
+  cls.prototype.constructor = cls;
+}
+function mixinHard(cls, mixin) {
+  mixinPropertiesHard(mixin.prototype, cls.prototype);
   cls.prototype.constructor = cls;
 }
 
@@ -374,7 +388,8 @@
   return {
     inherit: inherit,
     inheritMany: inheritMany,
-    mixin: mixin,
+    mixin: mixinEasy,
+    mixinHard: mixinHard,
     installStaticTearOff: installStaticTearOff,
     installInstanceTearOff: installInstanceTearOff,
 
@@ -777,7 +792,9 @@
 
       'call0selector': js.quoteName(call0Name),
       'call1selector': js.quoteName(call1Name),
-      'call2selector': js.quoteName(call2Name)
+      'call2selector': js.quoteName(call2Name),
+
+      'legacyJavaScript': _options.features.legacyJavaScript.isEnabled,
     });
     // We assume emitMainFragment will be the last piece of code we emit.
     finalizeCode(mainResourceName, mainCode, holderCode, finalizeHolders: true);
@@ -1211,7 +1228,9 @@
         collect(cls);
         if (cls.mixinClass != null) {
           js.Statement statement = js.js.statement('#(#, #)', [
-            locals.find('_mixin', 'hunkHelpers.mixin'),
+            _hasSimpleMixin(cls)
+                ? locals.find('_mixin', 'hunkHelpers.mixin')
+                : locals.find('_mixinHard', 'hunkHelpers.mixinHard'),
             classReference(cls),
             classReference(cls.mixinClass),
           ]);
@@ -1264,6 +1283,30 @@
     return wrapPhase('inheritance', statements);
   }
 
+  /// Determines if the mixin methods can be applied to a mixin application
+  /// class by a simple copy, or whether the class defines properties that would
+  /// be clobbered by block-copying the mixin's properties, so a slower checking
+  /// copy is needed.
+  bool _hasSimpleMixin(Class cls) {
+    List<Method> allMethods(Class cls) {
+      return [
+        ...cls.methods,
+        ...cls.checkedSetters,
+        ...cls.isChecks,
+        ...cls.callStubs,
+        ...cls.noSuchMethodStubs,
+        ...cls.gettersSetters
+      ];
+    }
+
+    final clsMethods = allMethods(cls);
+    if (clsMethods.isEmpty) return true;
+    // TODO(sra): Compare methods with those of `cls.mixinClass` to see if the
+    // methods (and hence properties) will actually clash. If they are
+    // non-overlapping, a simple copy might still be possible.
+    return false;
+  }
+
   /// Emits the setup of method aliases.
   ///
   /// This step consists of simply copying JavaScript functions to their
diff --git a/tools/VERSION b/tools/VERSION
index e72b70a..1cb0e87 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 155
+PRERELEASE 156
 PRERELEASE_PATCH 0
\ No newline at end of file