@@ -1373,10 +1373,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
13731373 if (nonExternalProperties.isNotEmpty) {
13741374 // Note that this class has no heritage. This class should never be used
13751375 // as a type. It's merely a placeholder for static members.
1376- var body = < js_ast.Statement > [
1377- _emitClassStatement (c, className, null , nonExternalProperties)
1378- .toStatement ()
1379- ];
1376+ var body = _emitClassStatement (c, className, null , nonExternalProperties);
13801377 var typeFormals = c.typeParameters;
13811378 if (typeFormals.isNotEmpty) {
13821379 var genericClassStmts =
@@ -1398,19 +1395,40 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
13981395 ];
13991396 }
14001397
1401- js_ast.Statement _emitClassStatement (Class c, js_ast.Expression className,
1402- js_ast.Expression ? heritage, List <js_ast.Property > properties) {
1403- var classIdentifier = _emitScopedId (getLocalClassName (c));
1398+ List <js_ast.Statement > _emitClassStatement (
1399+ Class c,
1400+ js_ast.Expression className,
1401+ js_ast.Expression ? heritage,
1402+ List <js_ast.Property > properties) {
1403+ var localClassName = getLocalClassName (c);
1404+ var classIdentifier = _emitScopedId (localClassName);
1405+ var classDefIdentifier = _emitScopedId ('_$localClassName ' );
1406+ // In order to ensure that 'super' binds to the correct target across hot
1407+ // reloads, we rebind its pre-assigned class definition's prototype during
1408+ // link phase (alongside the embedder-resolved class definition's).
1409+ var referencesSuperKeyword = hasSuper (c);
14041410 if (_options.emitDebugSymbols) classIdentifiers[c] = classIdentifier;
14051411 if (heritage != null ) {
1412+ if (referencesSuperKeyword) {
1413+ _classExtendsLinks.add (_runtimeStatement (
1414+ 'classExtends(#, #)' , [classDefIdentifier, heritage]));
1415+ }
14061416 _classExtendsLinks
14071417 .add (_runtimeStatement ('classExtends(#, #)' , [className, heritage]));
14081418 }
14091419 var classExpr = js_ast.ClassExpression (classIdentifier, null , properties);
14101420 var libraryExpr = (className as js_ast.PropertyAccess ).receiver;
14111421 var propertyExpr = className.selector;
1412- return _runtimeStatement (
1413- 'declareClass(#, #, #)' , [libraryExpr, propertyExpr, classExpr]);
1422+ return referencesSuperKeyword
1423+ ? [
1424+ js.statement ('let # = #;' , [classDefIdentifier, classExpr]),
1425+ _runtimeStatement ('declareClass(#, #, #)' ,
1426+ [libraryExpr, propertyExpr, classDefIdentifier])
1427+ ]
1428+ : [
1429+ _runtimeStatement (
1430+ 'declareClass(#, #, #)' , [libraryExpr, propertyExpr, classExpr])
1431+ ];
14141432 }
14151433
14161434 /// Like [_emitClassStatement] but emits a Dart 2.1 mixin represented by
@@ -1449,7 +1467,7 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
14491467 var staticProperties = properties.where ((m) => m.isStatic).toList ();
14501468 var instanceProperties = properties.where ((m) => ! m.isStatic).toList ();
14511469
1452- body.add (_emitClassStatement (c, className, heritage, staticProperties));
1470+ body.addAll (_emitClassStatement (c, className, heritage, staticProperties));
14531471 var superclassId = _emitScopedId (getLocalClassName (c.superclass! ));
14541472 var classId = className is js_ast.Identifier
14551473 ? className
@@ -1484,14 +1502,14 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
14841502 void _defineClass (Class c, js_ast.Expression className,
14851503 List <js_ast.Property > properties, List <js_ast.Statement > body) {
14861504 if (c == _coreTypes.objectClass) {
1487- body.add (_emitClassStatement (c, className, null , properties));
1505+ body.addAll (_emitClassStatement (c, className, null , properties));
14881506 return ;
14891507 }
14901508
1491- js_ast.Expression emitClassRef (InterfaceType t) {
1492- // TODO(jmesserly): investigate this. It seems like `lazyJSType` is
1493- // invalid for use in an `extends` clause, hence this workaround.
1494- return _emitJSInterop (t.classNode) ?? _emitClassRef (t);
1509+ js_ast.Expression emitClassRef (InterfaceType t,
1510+ { bool resolvedFromEmbedder = false }) {
1511+ return _emitJSInterop (t.classNode) ??
1512+ _emitClassRef (t, resolvedFromEmbedder : resolvedFromEmbedder );
14951513 }
14961514
14971515 // Find the real (user declared) superclass and the list of mixins.
@@ -1563,6 +1581,10 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
15631581
15641582 // Unroll mixins.
15651583 var baseClass = emitClassRef (supertype);
1584+ // The SDK is never hot reloaded, so we can avoid the overhead of
1585+ // resolving their classes through the embedder.
1586+ var embedderResolvedBaseClass =
1587+ emitClassRef (supertype, resolvedFromEmbedder: ! _isBuildingSdk);
15661588
15671589 // TODO(jmesserly): we need to unroll kernel mixins because the synthetic
15681590 // classes lack required synthetic members, such as constructors.
@@ -1622,19 +1644,25 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
16221644 js_ast.ClassExpression (
16231645 _emitScopedId (mixinName), null , forwardingMethodStubs)
16241646 ]));
1625- _classExtendsLinks
1626- . add ( _runtimeStatement ( 'classExtends(#, #)' , [mixinId, baseClass ]));
1647+ _classExtendsLinks. add ( _runtimeStatement (
1648+ 'classExtends(#, #)' , [mixinId, embedderResolvedBaseClass ]));
16271649 emitMixinConstructors (mixinId, superclass, mixinClass, mixinType);
16281650 hasUnnamedSuper = hasUnnamedSuper || _hasUnnamedConstructor (mixinClass);
1629- _mixinApplicationLinks.add (_runtimeStatement (
1630- 'applyMixin(#, #)' , [mixinId, emitClassRef (mixinType)]));
1651+ // The SDK is never hot reloaded, so we can avoid the overhead of
1652+ // resolving their classes through the embedder.
1653+ _mixinApplicationLinks.add (_runtimeStatement ('applyMixin(#, #)' , [
1654+ mixinId,
1655+ emitClassRef (mixinType, resolvedFromEmbedder: ! _isBuildingSdk)
1656+ ]));
16311657 baseClass = mixinId;
1658+ embedderResolvedBaseClass = mixinId;
16321659 }
16331660
16341661 if (c.isMixinDeclaration && ! c.isMixinClass) {
16351662 _emitMixinStatement (c, className, baseClass, properties, body);
16361663 } else {
1637- body.add (_emitClassStatement (c, className, baseClass, properties));
1664+ body.addAll (_emitClassStatement (
1665+ c, className, embedderResolvedBaseClass, properties));
16381666 }
16391667
16401668 _classEmittingExtends = savedTopLevelClass;
@@ -3368,16 +3396,27 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
33683396 /// Emit the top-level name associated with [n] , which should not be an
33693397 /// external interop member.
33703398 js_ast.PropertyAccess _emitTopLevelNameNoExternalInterop (NamedNode n,
3371- {String suffix = '' }) {
3399+ {String suffix = '' , bool resolvedFromEmbedder = false }) {
33723400 // Some native tests use top-level native methods.
33733401 var isTopLevelNative = n is Member && isNative (n);
33743402 return js_ast.PropertyAccess (
33753403 isTopLevelNative
33763404 ? _runtimeCall ('global.self' )
3377- : _emitLibraryName (getLibrary (n)),
3405+ : (resolvedFromEmbedder
3406+ ? _emitEmbedderResolvedLibrary (getLibrary (n))
3407+ : _emitLibraryName (getLibrary (n))),
33783408 _emitTopLevelMemberName (n, suffix: suffix));
33793409 }
33803410
3411+ /// Emits [library] fully resolved via the Dart Dev Embedder.
3412+ ///
3413+ /// Used when the 'current' hot reload's generation of a library needs to be
3414+ /// resolved.
3415+ js_ast.Expression _emitEmbedderResolvedLibrary (Library library) {
3416+ var libraryName = js.string ('${library .importUri }' );
3417+ return js.call ('dartDevEmbedder.importLibrary(#)' , [libraryName]);
3418+ }
3419+
33813420 /// Emits the member name portion of a top-level member.
33823421 ///
33833422 /// NOTE: usually you should use [_emitTopLevelName] instead of this. This
@@ -3817,21 +3856,19 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
38173856 /// Note that for `package:js` types, this will emit the class we emitted
38183857 /// using `_emitJSInteropClassNonExternalMembers` , and not the runtime type
38193858 /// that we synthesize for `package:js` types.
3820- js_ast.Expression _emitClassRef (InterfaceType type) {
3821- if (! _emittingClassExtends && type.typeArguments.isNotEmpty) {
3822- var genericName = _emitTopLevelNameNoExternalInterop (type.classNode);
3823- return js.call ('#' , [genericName]);
3824- }
3825- return _emitTopLevelNameNoExternalInterop (type.classNode);
3826- }
3859+ ///
3860+ /// [resolvedFromEmbedder] looks up [type] via the embedder, which retrieves
3861+ /// the correct library in the context of hot reload. This should not be set
3862+ /// for external classes.
3863+ js_ast.Expression _emitClassRef (InterfaceType type,
3864+ {bool resolvedFromEmbedder = false }) =>
3865+ _emitTopLevelNameNoExternalInterop (type.classNode,
3866+ resolvedFromEmbedder: resolvedFromEmbedder);
38273867
38283868 Never _typeCompilationError (DartType type, String description) =>
38293869 throw UnsupportedError ('$description Encountered while compiling '
38303870 '${_currentLibrary !.fileUri }, which contains the type: $type .' );
38313871
3832- bool get _emittingClassExtends =>
3833- _currentClass != null && identical (_currentClass, _classEmittingExtends);
3834-
38353872 /// Emits an expression that lets you access statics on a [type] from code.
38363873 js_ast.Expression _emitConstructorName (InterfaceType type, Member c) {
38373874 var isSyntheticDefault =
0 commit comments