@@ -701,13 +701,27 @@ class CodeGenerator extends GeneralizingAstVisitor
701
701
var fields = < FieldDeclaration > [];
702
702
var staticFields = < FieldDeclaration > [];
703
703
var methods = < MethodDeclaration > [];
704
+
705
+ // True if a "call" method or getter exists. This can also be
706
+ // "noSuchMethod" method, because nSM could implement "call".
707
+ bool isCallable = false ;
704
708
for (var member in node.members) {
705
709
if (member is ConstructorDeclaration ) {
706
710
ctors.add (member);
707
711
} else if (member is FieldDeclaration ) {
708
712
(member.isStatic ? staticFields : fields).add (member);
709
713
} else if (member is MethodDeclaration ) {
710
714
methods.add (member);
715
+ var name = member.name.name;
716
+ if (name == 'call' && ! member.isSetter) {
717
+ isCallable = true ;
718
+ } else if (name == 'noSuchMethod' &&
719
+ ! member.isGetter &&
720
+ ! member.isSetter &&
721
+ // Exclude SDK because we know they don't use nSM to implement call.
722
+ ! classElem.library.source.isInSystemLibrary) {
723
+ isCallable = true ;
724
+ }
711
725
}
712
726
}
713
727
@@ -738,15 +752,15 @@ class CodeGenerator extends GeneralizingAstVisitor
738
752
_emitSuperHelperSymbols (_superHelperSymbols, body);
739
753
740
754
// Emit the class, e.g. `core.Object = class Object { ... }`
741
- _defineClass (classElem, className, classExpr, body);
755
+ _defineClass (classElem, className, classExpr, isCallable, body);
742
756
743
757
// Emit things that come after the ES6 `class ... { ... }`.
744
758
var jsPeerName = _getJSPeerName (classElem);
745
759
_setBaseClass (classElem, className, jsPeerName, body);
746
760
747
761
_emitClassTypeTests (classElem, className, body);
748
762
749
- _defineNamedConstructors (ctors, body, className);
763
+ _defineNamedConstructors (ctors, body, className, isCallable );
750
764
_emitVirtualFieldSymbols (virtualFieldSymbols, body);
751
765
_emitClassSignature (methods, classElem, ctors, extensions, className, body);
752
766
_defineExtensionMembers (extensions, className, body);
@@ -764,6 +778,36 @@ class CodeGenerator extends GeneralizingAstVisitor
764
778
return _statement (body);
765
779
}
766
780
781
+ /// Emits code to support a class with a "call" method and an unnamed
782
+ /// constructor.
783
+ ///
784
+ /// This ensures instances created by the unnamed constructor are functions.
785
+ /// Named constructors are handled elsewhere, see [_defineNamedConstructors] .
786
+ JS .Expression _emitCallableClass (JS .ClassExpression classExpr,
787
+ ConstructorElement unnamedCtor) {
788
+ var ctor = new JS .NamedFunction (
789
+ classExpr.name, _emitCallableClassConstructor (unnamedCtor));
790
+
791
+ // Name the constructor function the same as the class.
792
+ return js.call ('dart.callableClass(#, #)' , [ctor, classExpr]);
793
+ }
794
+
795
+ JS .Fun _emitCallableClassConstructor (
796
+ ConstructorElement ctor) {
797
+
798
+ return js.call (
799
+ r'''function (...args) {
800
+ const self = this;
801
+ function call(...args) {
802
+ return self.call.apply(self, args);
803
+ }
804
+ call.__proto__ = this.__proto__;
805
+ call.#.apply(call, args);
806
+ return call;
807
+ }''' ,
808
+ [_constructorName (ctor)]);
809
+ }
810
+
767
811
void _emitClassTypeTests (ClassElement classElem, JS .Expression className,
768
812
List <JS .Statement > body) {
769
813
if (classElem == objectClass) {
@@ -978,12 +1022,26 @@ class CodeGenerator extends GeneralizingAstVisitor
978
1022
}
979
1023
}
980
1024
981
- void _defineClass (ClassElement classElem, JS .Expression className,
982
- JS .ClassExpression classExpr, List <JS .Statement > body) {
1025
+ void _defineClass (
1026
+ ClassElement classElem,
1027
+ JS .Expression className,
1028
+ JS .ClassExpression classExpr,
1029
+ bool isCallable,
1030
+ List <JS .Statement > body) {
1031
+ JS .Expression callableClass;
1032
+ if (isCallable && classElem.unnamedConstructor != null ) {
1033
+ callableClass = _emitCallableClass (
1034
+ classExpr, classElem.unnamedConstructor);
1035
+ }
1036
+
983
1037
if (classElem.typeParameters.isNotEmpty) {
984
- body.add (new JS .ClassDeclaration (classExpr));
1038
+ if (callableClass != null ) {
1039
+ body.add (js.statement ('const # = #;' , [classExpr.name, callableClass]));
1040
+ } else {
1041
+ body.add (new JS .ClassDeclaration (classExpr));
1042
+ }
985
1043
} else {
986
- body.add (js.statement ('# = #;' , [className, classExpr]));
1044
+ body.add (js.statement ('# = #;' , [className, callableClass ?? classExpr]));
987
1045
}
988
1046
}
989
1047
@@ -1400,12 +1458,23 @@ class CodeGenerator extends GeneralizingAstVisitor
1400
1458
}
1401
1459
}
1402
1460
1403
- void _defineNamedConstructors (List <ConstructorDeclaration > ctors,
1404
- List <JS .Statement > body, JS .Expression className) {
1461
+ void _defineNamedConstructors (
1462
+ List <ConstructorDeclaration > ctors,
1463
+ List <JS .Statement > body,
1464
+ JS .Expression className,
1465
+ bool isCallable) {
1466
+ var code = isCallable
1467
+ ? 'dart.defineNamedConstructorCallable(#, #, #);'
1468
+ : 'dart.defineNamedConstructor(#, #)' ;
1469
+
1405
1470
for (ConstructorDeclaration member in ctors) {
1406
1471
if (member.name != null && member.factoryKeyword == null ) {
1407
- body.add (js.statement ('dart.defineNamedConstructor(#, #);' ,
1408
- [className, _constructorName (member.element)]));
1472
+ var args = [className, _constructorName (member.element)];
1473
+ if (isCallable) {
1474
+ args.add (_emitCallableClassConstructor (member.element));
1475
+ }
1476
+
1477
+ body.add (js.statement (code, args));
1409
1478
}
1410
1479
}
1411
1480
}
0 commit comments