@@ -232,6 +232,20 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
232232 Map <Class , js_ast.Identifier > get classIdentifiers =>
233233 _symbolData.classIdentifiers;
234234
235+ /// Maps every mixin application to a unique identifier.
236+ ///
237+ /// A mixin application is represented as a (mixin, class) pair, where
238+ /// 'mixin' is being mixed into 'class'. Anonymous mixins are already
239+ /// unique per mixin application and so pass themselves in as both 'mixin'
240+ /// and 'class'.
241+ ///
242+ /// This mapping is used when generating super property getters in mixins.
243+ final Map <(Class , Class ), js_ast.Identifier > _mixinCache = {};
244+
245+ /// Records a reference to a mixin application's passed in superclass.
246+ /// (see [_emitMixinStatement] ).
247+ final Map <Class , js_ast.Identifier > _mixinSuperclassCache = {};
248+
235249 /// Maps each class `Member` node compiled in the module to the name used for
236250 /// the member in JavaScript.
237251 ///
@@ -1173,6 +1187,13 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
11731187 // a simpler representation within instance members of the class.
11741188 _currentTypeEnvironment = ClassTypeEnvironment (c.typeParameters);
11751189
1190+ // Store identifiers for a mixin application's passed in superclass.
1191+ // (see [_emitMixinStatement]).
1192+ if (c.isMixinDeclaration && ! c.isMixinClass) {
1193+ _mixinSuperclassCache.putIfAbsent (
1194+ c, () => _emitScopedId (getLocalClassName (c.superclass! )));
1195+ }
1196+
11761197 // Mixins are unrolled in _defineClass.
11771198 if (! c.isAnonymousMixin) {
11781199 // If this class is annotated with `@JS`, then we only need to emit the
@@ -1463,7 +1484,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
14631484 var instanceProperties = properties.where ((m) => ! m.isStatic).toList ();
14641485
14651486 body.addAll (_emitClassStatement (c, className, heritage, staticProperties));
1466- var superclassId = _emitScopedId ( getLocalClassName (c.superclass ! )) ;
1487+ var superclassId = _mixinSuperclassCache[c] ! ;
14671488 var classId = className is js_ast.Identifier
14681489 ? className
14691490 : _emitScopedId (getLocalClassName (c));
@@ -1590,9 +1611,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
15901611 var mixinClass = m.isAnonymousMixin ? m.mixedInClass! : m;
15911612 var mixinType =
15921613 _hierarchy.getClassAsInstanceOf (c, mixinClass)! .asInterfaceType;
1593- var mixinName =
1594- '${getLocalClassName (superclass )}_${getLocalClassName (mixinClass )}' ;
1595- var mixinId = _emitScopedId ('$mixinName \$ ' );
1614+ var mixinId = _emitMixinId (m, m.isAnonymousMixin ? m : c);
15961615 // Collect all forwarding stub members from anonymous mixins classes.
15971616 // These can contain covariant parameter checks that need to be applied.
15981617 var savedClassProperties = _classProperties;
@@ -1630,24 +1649,36 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
16301649 _currentTypeEnvironment = savedTypeEnvironment;
16311650 _classProperties = savedClassProperties;
16321651
1633- // Bind the mixin class to a name to workaround a V8 bug with es6 classes
1634- // and anonymous function names.
1635- // TODO(leafp:) Eliminate this once the bug is fixed:
1636- // https://bugs.chromium.org/p/v8/issues/detail?id=7069
1637- body.add (js.statement ('const # = #' , [
1652+ // Mixins need to be exposed in this library in case they are
1653+ // referenced in a super getter.
1654+ // TODO(markzipan): We originally bound mixin classes to a temporary as a
1655+ // workaround for a now-resolved Chrome issue. However, a side effect of
1656+ // this operation is that mixin IDs are renamed by the local visitor. We
1657+ // can remove this hoisting after we give mixins unique names.
1658+ body.add (js.statement ('let # = #' , [
16381659 mixinId,
1639- js_ast.ClassExpression (
1640- _emitScopedId (mixinName), null , forwardingMethodStubs)
1660+ _runtimeCall ('declareClass(#, #, #)' , [
1661+ _emitLibraryName (_currentLibrary! ),
1662+ js.string (mixinId.name),
1663+ js_ast.ClassExpression (
1664+ _emitScopedId ('${mixinId .name }\$ ' ),
1665+ null ,
1666+ forwardingMethodStubs,
1667+ )
1668+ ])
16411669 ]));
1670+
16421671 _classExtendsLinks.add (_runtimeStatement (
16431672 'classExtends(#, #)' , [mixinId, embedderResolvedBaseClass]));
16441673 emitMixinConstructors (mixinId, superclass, mixinClass, mixinType);
16451674 hasUnnamedSuper = hasUnnamedSuper || _hasUnnamedConstructor (mixinClass);
1675+ var mixinTargetLabel = js.string (fullyResolvedMixinClassLabel (m));
16461676 // The SDK is never hot reloaded, so we can avoid the overhead of
16471677 // resolving their classes through the embedder.
1648- _mixinApplicationLinks.add (_runtimeStatement ('applyMixin(#, #)' , [
1678+ _mixinApplicationLinks.add (_runtimeStatement ('applyMixin(#, #, # )' , [
16491679 mixinId,
1650- emitClassRef (mixinType, resolvedFromEmbedder: ! _isBuildingSdk)
1680+ emitClassRef (mixinType, resolvedFromEmbedder: ! _isBuildingSdk),
1681+ mixinTargetLabel
16511682 ]));
16521683 baseClass = mixinId;
16531684 embedderResolvedBaseClass = mixinId;
@@ -1810,6 +1841,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
18101841 var staticMethods = < js_ast.Property > [];
18111842 var instanceMethods = < js_ast.Property > [];
18121843 var instanceMethodsDefaultTypeArgs = < js_ast.Property > [];
1844+ var methodsImmediateTarget = < js_ast.Property > [];
18131845 var staticGetters = < js_ast.Property > [];
18141846 var instanceGetters = < js_ast.Property > [];
18151847 var staticSetters = < js_ast.Property > [];
@@ -1863,9 +1895,16 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
18631895 var needsSignature = memberOverride == null ||
18641896 reifiedType != _memberRuntimeType (memberOverride, c);
18651897
1898+ var memberName = _declareMemberName (member,
1899+ useExtension: _isObjectMethodTearoff (member.name.text));
1900+ if (! member.isAccessor) {
1901+ var immediateTarget = js.string (fullyResolvedTargetLabel (member));
1902+ methodsImmediateTarget
1903+ .add (js_ast.Property (memberName, immediateTarget));
1904+ }
1905+
18661906 if (needsSignature) {
18671907 js_ast.Expression type;
1868- var memberName = _declareMemberName (member);
18691908 if (member.isAccessor) {
18701909 // These signatures are used for dynamic access and to inform the
18711910 // debugger. The `arrayRti` accessor is only used by the dart:_rti
@@ -1918,6 +1957,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
19181957
19191958 emitSignature ('Method' , instanceMethods);
19201959 emitSignature ('MethodsDefaultTypeArg' , instanceMethodsDefaultTypeArgs);
1960+ emitSignature ('MethodsImmediateTarget' , methodsImmediateTarget);
19211961 // TODO(40273) Skip for all statics when the debugger consumes signature
19221962 // information from symbol files.
19231963 emitSignature ('StaticMethod' , staticMethods);
@@ -3136,12 +3176,14 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
31363176 ///
31373177 /// Unlike call sites, we always have an element available, so we can use it
31383178 /// directly rather than computing the relevant options for [_emitMemberName] .
3139- js_ast.Expression _declareMemberName (Member m, {bool ? useExtension}) {
3179+ js_ast.Expression _declareMemberName (Member m, {bool useExtension = false }) {
31403180 var c = m.enclosingClass;
3141- return _emitMemberName (m.name.text,
3181+ var name = m.name.text;
3182+ var actualUseExtension =
3183+ useExtension || (c != null && _extensionTypes.isNativeClass (c));
3184+ return _emitMemberName (name,
31423185 isStatic: m is Field ? m.isStatic : (m as Procedure ).isStatic,
3143- useExtension:
3144- useExtension ?? c != null && _extensionTypes.isNativeClass (c),
3186+ useExtension: actualUseExtension,
31453187 member: m);
31463188 }
31473189
@@ -5431,7 +5473,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
54315473 }
54325474 var jsMemberName = _emitMemberName (memberName, member: member);
54335475 if (_reifyTearoff (member)) {
5434- return _runtimeCall ('tearoff(#, #)' , [jsReceiver, jsMemberName]);
5476+ return _runtimeCall ('tearoff(#, null, #)' , [jsReceiver, jsMemberName]);
54355477 }
54365478 var jsPropertyAccess = js_ast.PropertyAccess (jsReceiver, jsMemberName);
54375479 return isJsMember (member)
@@ -5572,11 +5614,41 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
55725614 return _emitSuperPropertyGet (node.interfaceTarget);
55735615 }
55745616
5617+ /// Emits a reference to a distinct mixin application, represented by
5618+ /// a [mixedInClass] being mixed into [baseClass] .
5619+ ///
5620+ /// Anonymous mixins should pass themselves as [baseClass] since they are
5621+ /// already uniquely generated per distinct mixin application
5622+ js_ast.Identifier _emitMixinId (Class mixedInClass, Class baseClass) {
5623+ var mixinName = mixedInClass.name;
5624+ if (! mixedInClass.isAnonymousMixin) {
5625+ mixinName += '#${baseClass .name }' ;
5626+ }
5627+ return _mixinCache
5628+ .putIfAbsent ((mixedInClass, baseClass), () => _emitScopedId (mixinName));
5629+ }
5630+
55755631 js_ast.Expression _emitSuperPropertyGet (Member target) {
55765632 if (_reifyTearoff (target)) {
55775633 if (_superAllowed) {
55785634 var jsTarget = _emitSuperTarget (target);
5579- return _runtimeCall ('bind(this, #, #)' , [jsTarget.selector, jsTarget]);
5635+ var jsName = _declareMemberName (target);
5636+ var enclosingClass = target.enclosingClass! ;
5637+ js_ast.Expression ? supertypeReference =
5638+ _mixinSuperclassCache[_currentClass! ];
5639+ if (supertypeReference == null ) {
5640+ if (enclosingClass.isAnonymousMixin) {
5641+ var mixinId = _emitMixinId (enclosingClass, enclosingClass);
5642+ var enclosingLibrary = _emitLibraryName (getLibrary (enclosingClass));
5643+ supertypeReference = js_ast.PropertyAccess (
5644+ enclosingLibrary, js.string (mixinId.name));
5645+ } else {
5646+ supertypeReference =
5647+ _emitTopLevelNameNoExternalInterop (enclosingClass);
5648+ }
5649+ }
5650+ return _runtimeCall (
5651+ 'bind(this, #, #, #)' , [supertypeReference, jsName, jsTarget]);
55805652 } else {
55815653 return _emitSuperTearoff (target);
55825654 }
@@ -5625,7 +5697,9 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
56255697 var property = propertyAccessor.selector;
56265698 var result = js.call ('#.#' , [context, property]);
56275699 if (_reifyTearoff (target)) {
5628- return _runtimeCall ('staticTearoff(#, #)' , [context, property]);
5700+ var targetLabel = js.string (fullyResolvedTargetLabel (target));
5701+ return _runtimeCall (
5702+ 'staticTearoff(#, #, #)' , [context, targetLabel, property]);
56295703 }
56305704 return result;
56315705 }
@@ -6259,8 +6333,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
62596333 /// Emits the [js_ast.PropertyAccess] for accessors or method calls to
62606334 /// [jsTarget] .[jsName] , replacing `super` if it is not allowed in scope.
62616335 js_ast.PropertyAccess _emitSuperTarget (Member member, {bool setter = false }) {
6262- var jsName = _emitMemberName (member.name.text, member: member);
6263- // Optimize access to non-virtual fields, if allowed in the current context.
6336+ var jsName = _declareMemberName (member);
62646337 if (_optimizeNonVirtualFieldAccess &&
62656338 member is Field &&
62666339 ! _virtualFields.isVirtual (member)) {
@@ -6316,18 +6389,42 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
63166389 return js_ast.PropertyAccess (js_ast.This (), jsMethod.name);
63176390 }
63186391
6392+ /// Generates a special string used for identifying a torn off member [m] .
6393+ ///
6394+ /// This tag is used for determining tearoff equality. We attach these tags
6395+ /// at tearoff time for static tearoffs and in the method signature for
6396+ /// dynamic tearoffs.
6397+ String fullyResolvedTargetLabel (Member m) {
6398+ return '${m .enclosingLibrary .importUri }:${m .enclosingClass ?.name ?? "" }' ;
6399+ }
6400+
6401+ /// Generates a special string used for identifying class [c] 's applied mixed
6402+ /// in members.
6403+ ///
6404+ /// This tag is used for determining tearoff equality. We attach these tags
6405+ /// at tearoff time for static tearoffs and in the method signature for
6406+ /// dynamic tearoffs.
6407+ String fullyResolvedMixinClassLabel (Class c) {
6408+ return '${c .enclosingLibrary .importUri }:${c .name }' ;
6409+ }
6410+
63196411 /// Generates a helper method that is inserted into the class that binds a
63206412 /// tearoff of [member] from `super` and returns a call to the helper.
63216413 ///
63226414 /// This method assumes `super` is not allowed in the current context.
63236415 // TODO(nshahan) Replace with a kernel transform and synthetic method filters
63246416 // for devtools.
63256417 js_ast.Expression _emitSuperTearoff (Member member) {
6326- var jsName = _emitMemberName (member.name.text, member : member);
6418+ var jsName = _declareMemberName ( member);
63276419 var name = '_#super#tearOff#${member .name .text }' ;
63286420 var jsMethod = _superHelpers.putIfAbsent (name, () {
6329- var jsReturnValue =
6330- _runtimeCall ('bind(this, #, super[#])' , [jsName, jsName]);
6421+ var superclass = member.enclosingClass? .superclass;
6422+ var supertypeReference = superclass == null
6423+ ? js_ast.LiteralNull ()
6424+ : _mixinSuperclassCache[member.enclosingClass! ] ??
6425+ _emitTopLevelNameNoExternalInterop (superclass);
6426+ var jsReturnValue = _runtimeCall (
6427+ 'bind(this, #, #, super[#])' , [supertypeReference, jsName, jsName]);
63316428 var fn = js.fun ('function() { return #; }' , [jsReturnValue]);
63326429 name = js_ast.friendlyNameForDartOperator[name] ?? name;
63336430 return js_ast.Method (_emitScopedId (name), fn);
0 commit comments