@@ -3,7 +3,7 @@ import * as ts from 'typescript';
3
3
import * as base from './base' ;
4
4
import { FacadeConverter } from './facade_converter' ;
5
5
import { Transpiler } from './main' ;
6
- import { MergedParameter , MergedType , MergedTypeParameters } from './merge' ;
6
+ import { MergedMember , MergedParameter , MergedType , MergedTypeParameters } from './merge' ;
7
7
8
8
export function isFunctionLikeProperty (
9
9
decl : ts . VariableDeclaration | ts . ParameterDeclaration | ts . PropertyDeclaration |
@@ -20,8 +20,9 @@ export function isFunctionLikeProperty(
20
20
export default class DeclarationTranspiler extends base . TranspilerBase {
21
21
private tc : ts . TypeChecker ;
22
22
private extendsClass = false ;
23
+ private visitPromises = false ;
23
24
private containsPromises = false ;
24
- private promiseMethods : Set < ts . FunctionLikeDeclaration > = new Set ( ) ;
25
+ private promiseMethods : ts . SignatureDeclaration [ ] = [ ] ;
25
26
26
27
static NUM_FAKE_REST_PARAMETERS = 5 ;
27
28
@@ -243,7 +244,16 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
243
244
}
244
245
}
245
246
246
- visitMergingOverloads ( members : ts . NodeArray < ts . Node > ) {
247
+ /**
248
+ * Visits an array of class members and merges overloads.
249
+ *
250
+ * @returns An updated version of the members array. All overloaded methods are grouped into
251
+ * MergedMember objects that contain all original declarations, as well as the merged result.
252
+ * Other non-overloaded members are represented by MergedMembers with only one constituent,
253
+ * which is the single declaration of the member.
254
+ */
255
+ visitMergingOverloads ( members : ts . NodeArray < ts . Node > ) : MergedMember [ ] {
256
+ const result : MergedMember [ ] = [ ] ;
247
257
// TODO(jacobr): merge method overloads.
248
258
let groups : Map < string , ts . Node [ ] > = new Map ( ) ;
249
259
let orderedGroups : Array < ts . Node [ ] > = [ ] ;
@@ -315,9 +325,24 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
315
325
group . push ( node ) ;
316
326
} ) ;
317
327
318
- orderedGroups . forEach ( ( group : Array < ts . Node > ) => {
328
+ orderedGroups . forEach ( ( group : Array < ts . SignatureDeclaration > ) => {
329
+ const first = group [ 0 ] ;
330
+ // If the methods in this group return Promises and this.visitPromises is false, skip
331
+ // visiting these methods and add them to this.promiseMethods. If the methods in this group
332
+ // return Promises and this.visitPromises is true, it means that this function is being called
333
+ // from emitMethodsAsExtensions and the methods should now be visited.
334
+ if ( ! this . visitPromises && base . isPromise ( first . type ) ) {
335
+ if ( ! this . containsPromises ) {
336
+ this . containsPromises = true ;
337
+ }
338
+ group . forEach ( ( declaration : ts . SignatureDeclaration ) => {
339
+ this . promiseMethods . push ( declaration ) ;
340
+ } ) ;
341
+ return ;
342
+ }
319
343
if ( group . length === 1 ) {
320
- this . visit ( group [ 0 ] ) ;
344
+ this . visit ( first ) ;
345
+ result . push ( new MergedMember ( group , first ) ) ;
321
346
return ;
322
347
}
323
348
group . forEach ( ( fn : ts . Node ) => {
@@ -330,7 +355,6 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
330
355
this . maybeLineBreak ( ) ;
331
356
} ) ;
332
357
// TODO: actually merge.
333
- let first = < ts . SignatureDeclaration > group [ 0 ] ;
334
358
let kind = first . kind ;
335
359
let merged = < ts . SignatureDeclaration > ts . createNode ( kind ) ;
336
360
merged . parent = first . parent ;
@@ -384,7 +408,9 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
384
408
merged . typeParameters = mergedTypeParams . toTypeParameters ( ) ;
385
409
386
410
this . fc . visit ( merged ) ;
411
+ result . push ( new MergedMember ( group , merged ) ) ;
387
412
} ) ;
413
+ return result ;
388
414
}
389
415
390
416
@@ -686,16 +712,6 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
686
712
break ;
687
713
case ts . SyntaxKind . MethodSignature :
688
714
let methodSignatureDecl = < ts . FunctionLikeDeclaration > node ;
689
- if ( base . isPromise ( methodSignatureDecl . type ) ) {
690
- if ( this . promiseMethods . has ( methodSignatureDecl ) ) {
691
- break ;
692
- }
693
- if ( ! this . containsPromises ) {
694
- this . containsPromises = true ;
695
- }
696
- this . promiseMethods . add ( methodSignatureDecl ) ;
697
- break ;
698
- }
699
715
this . visitDeclarationMetadata ( methodSignatureDecl ) ;
700
716
this . visitFunctionLike ( methodSignatureDecl ) ;
701
717
break ;
@@ -865,15 +881,15 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
865
881
866
882
this . visitClassBody ( decl , name ) ;
867
883
this . emit ( '}\n' ) ;
868
- if ( this . promiseMethods . size ) {
884
+ if ( this . promiseMethods . length ) {
869
885
const visitName = ( ) => {
870
886
this . visitClassLikeName ( name , typeParameters , ts . createNodeArray ( ) , false ) ;
871
887
} ;
872
888
const visitNameOfExtensions = ( ) => {
873
889
this . visitClassLikeName ( name , typeParameters , ts . createNodeArray ( ) , true ) ;
874
890
} ;
875
891
this . emitMethodsAsExtensions ( name , visitName , visitNameOfExtensions , this . promiseMethods ) ;
876
- this . promiseMethods . clear ( ) ;
892
+ this . promiseMethods = [ ] ;
877
893
}
878
894
this . emit ( '\n' ) ;
879
895
}
@@ -949,15 +965,14 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
949
965
950
966
private emitMethodsAsExtensions (
951
967
className : ts . Identifier , visitName : ( ) => void , visitNameOfExtensions : ( ) => void ,
952
- methods : Set < ts . FunctionLikeDeclaration > ) {
968
+ methods : ts . SignatureDeclaration [ ] ) {
969
+ this . visitPromises = true ;
953
970
// Emit private class containing external methods
954
971
this . emit ( `@JS('${ base . ident ( className ) } ')` ) ;
955
972
this . emit ( `abstract class _` ) ;
956
973
visitName ( ) ;
957
974
this . emit ( '{' ) ;
958
- for ( const declaration of methods ) {
959
- this . visitFunctionLike ( declaration ) ;
960
- }
975
+ const mergedMembers = this . visitMergingOverloads ( ts . createNodeArray ( Array . from ( methods ) ) ) ;
961
976
this . emit ( '}\n' ) ;
962
977
963
978
// Emit extensions on public class to expose methods
@@ -966,7 +981,8 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
966
981
this . emit ( 'on' ) ;
967
982
visitName ( ) ;
968
983
this . emit ( '{' ) ;
969
- for ( const declaration of methods ) {
984
+ for ( const merged of mergedMembers ) {
985
+ const declaration = merged . mergedDeclaration ;
970
986
if ( ! base . isPromise ( declaration . type ) ) {
971
987
continue ;
972
988
}
@@ -981,10 +997,49 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
981
997
this . emit ( 'final _' ) ;
982
998
this . fc . visitTypeName ( className ) ;
983
999
this . emit ( 'tt = t;\n' ) ;
984
- this . emit ( `return promiseToFuture(tt.${ base . ident ( declaration . name ) } ` ) ;
985
- this . visitParameters ( declaration . parameters , { namesOnly : true } ) ;
986
- this . emit ( ');}\n' ) ;
1000
+ this . emitExtensionBody ( merged ) ;
1001
+ this . emit ( '}\n' ) ;
987
1002
}
988
1003
this . emit ( '}\n' ) ;
1004
+ this . visitPromises = false ;
1005
+ }
1006
+
1007
+ private emitExtensionBody ( { constituents, mergedDeclaration} : MergedMember ) {
1008
+ // Determine all valid arties of this method by going through the overloaded signatures
1009
+ const arities : Set < number > = new Set ( ) ;
1010
+ for ( const constituent of constituents ) {
1011
+ const arity = constituent . parameters . length ;
1012
+ arities . add ( arity ) ;
1013
+ }
1014
+ const sortedArities = Array . from ( arities ) . sort ( ) ;
1015
+ for ( const arity of sortedArities ) {
1016
+ if ( arity < mergedDeclaration . parameters . length ) {
1017
+ const firstOptionalIndex = arity ;
1018
+ const suppliedParameters = mergedDeclaration . parameters . slice ( 0 , firstOptionalIndex ) ;
1019
+ const omittedParameters = mergedDeclaration . parameters . slice (
1020
+ firstOptionalIndex , mergedDeclaration . parameters . length ) ;
1021
+ // Emit null checks to verify the number of omitted parameters
1022
+ this . emit ( 'if (' ) ;
1023
+ let isFirst = true ;
1024
+ for ( const omitted of omittedParameters ) {
1025
+ if ( isFirst ) {
1026
+ isFirst = false ;
1027
+ } else {
1028
+ this . emit ( '&&' ) ;
1029
+ }
1030
+ this . visit ( omitted . name ) ;
1031
+ this . emit ( '== null' ) ;
1032
+ }
1033
+ this . emit ( ') {' ) ;
1034
+ this . emit ( `return promiseToFuture(tt.${ base . ident ( mergedDeclaration . name ) } ` ) ;
1035
+ this . visitParameters ( ts . createNodeArray ( suppliedParameters ) , { namesOnly : true } ) ;
1036
+ this . emit ( '); }\n' ) ;
1037
+ } else {
1038
+ // No parameters were omitted, no null checks are necessary for this call
1039
+ this . emit ( `return promiseToFuture(tt.${ base . ident ( mergedDeclaration . name ) } ` ) ;
1040
+ this . visitParameters ( ts . createNodeArray ( mergedDeclaration . parameters ) , { namesOnly : true } ) ;
1041
+ this . emit ( ');\n' ) ;
1042
+ }
1043
+ }
989
1044
}
990
1045
}
0 commit comments