@@ -368,18 +368,20 @@ private void ExportModule(
368
368
s += $ "exportsValue = new JSModuleBuilder<{ ns } .{ moduleType . Name } >()";
369
369
s . IncreaseIndent ( ) ;
370
370
371
- // Export non-static members of the module class.
372
- foreach ( ISymbol ? member in moduleType . GetMembers ( )
373
- . Where ( ( m ) => m . DeclaredAccessibility == Accessibility . Public && ! m . IsStatic ) )
371
+ // Export public non-static members of the module class.
372
+ IEnumerable < ISymbol > members = moduleType . GetMembers ( )
373
+ . Where ( ( m ) => m . DeclaredAccessibility == Accessibility . Public && ! m . IsStatic ) ;
374
+
375
+ foreach ( IPropertySymbol property in members . OfType < IPropertySymbol > ( ) )
374
376
{
375
- if ( member is IMethodSymbol method && method . MethodKind == MethodKind . Ordinary )
376
- {
377
- ExportMethod ( ref s , method ) ;
378
- }
379
- else if ( member is IPropertySymbol property )
380
- {
381
- ExportProperty ( ref s , property ) ;
382
- }
377
+ ExportProperty ( ref s , property , GetExportName ( property ) ) ;
378
+ }
379
+
380
+ foreach ( IGrouping < string , IMethodSymbol > methodGroup in members . OfType < IMethodSymbol > ( )
381
+ . Where ( ( m ) => m . MethodKind == MethodKind . Ordinary )
382
+ . GroupBy ( GetExportName ) )
383
+ {
384
+ ExportMethod ( ref s , methodGroup , methodGroup . Key ) ;
383
385
}
384
386
}
385
387
else
@@ -401,18 +403,20 @@ private void ExportModule(
401
403
// Export tagged static properties as properties on the module.
402
404
ExportProperty ( ref s , exportProperty , exportName ) ;
403
405
}
404
- else if ( exportItem is IMethodSymbol exportMethod )
405
- {
406
- // Export tagged static methods as top-level functions on the module.
407
- ExportMethod ( ref s , exportMethod , exportName ) ;
408
- }
409
406
else if ( exportItem is ITypeSymbol exportDelegate &&
410
407
exportDelegate . TypeKind == TypeKind . Delegate )
411
408
{
412
409
ExportDelegate ( exportDelegate ) ;
413
410
}
414
411
}
415
412
413
+ // Export tagged static methods as top-level functions on the module.
414
+ foreach ( IGrouping < string , IMethodSymbol > methodGroup in exportItems . OfType < IMethodSymbol > ( )
415
+ . GroupBy ( GetExportName ) )
416
+ {
417
+ ExportMethod ( ref s , methodGroup , methodGroup . Key ) ;
418
+ }
419
+
416
420
if ( moduleType != null )
417
421
{
418
422
// Construct an instance of the custom module class when the module is initialized.
@@ -434,10 +438,8 @@ private void ExportModule(
434
438
private void ExportType (
435
439
ref SourceBuilder s ,
436
440
ITypeSymbol type ,
437
- string ? exportName = null )
441
+ string exportName )
438
442
{
439
- exportName ??= type . Name ;
440
-
441
443
string propertyAttributes = string . Empty ;
442
444
if ( type . ContainingType != null )
443
445
{
@@ -547,22 +549,15 @@ private void ExportMembers(
547
549
{
548
550
bool isStreamClass = typeof ( System . IO . Stream ) . IsAssignableFrom ( type . AsType ( ) ) ;
549
551
550
- foreach ( ISymbol member in type . GetMembers ( )
551
- . Where ( ( m ) => m . DeclaredAccessibility == Accessibility . Public ) )
552
- {
553
- if ( isStreamClass && ! member . IsStatic )
554
- {
555
- // Only static members on stream subclasses are exported to JS.
556
- continue ;
557
- }
552
+ IEnumerable < ISymbol > members = type . GetMembers ( )
553
+ . Where ( ( m ) => m . DeclaredAccessibility == Accessibility . Public )
554
+ . Where ( ( m ) => ! isStreamClass || m . IsStatic ) ;
558
555
559
- if ( member is IMethodSymbol method && method . MethodKind == MethodKind . Ordinary )
560
- {
561
- ExportMethod ( ref s , method ) ;
562
- }
563
- else if ( member is IPropertySymbol property )
556
+ foreach ( ISymbol member in members )
557
+ {
558
+ if ( member is IPropertySymbol property )
564
559
{
565
- ExportProperty ( ref s , property ) ;
560
+ ExportProperty ( ref s , property , GetExportName ( member ) ) ;
566
561
}
567
562
else if ( type . TypeKind == TypeKind . Enum && member is IFieldSymbol field )
568
563
{
@@ -571,42 +566,61 @@ private void ExportMembers(
571
566
}
572
567
else if ( member is INamedTypeSymbol nestedType )
573
568
{
574
- ExportType ( ref s , nestedType ) ;
569
+ ExportType ( ref s , nestedType , GetExportName ( member ) ) ;
575
570
}
576
571
}
572
+
573
+ foreach ( IGrouping < string , IMethodSymbol > methodGroup in members
574
+ . OfType < IMethodSymbol > ( ) . Where ( ( m ) => m . MethodKind == MethodKind . Ordinary )
575
+ . GroupBy ( GetExportName ) )
576
+ {
577
+ ExportMethod ( ref s , methodGroup , methodGroup . Key ) ;
578
+ }
577
579
}
578
580
579
581
/// <summary>
580
582
/// Generate code for a method exported on a class, struct, or module.
581
583
/// </summary>
582
584
private void ExportMethod (
583
585
ref SourceBuilder s ,
584
- IMethodSymbol method ,
585
- string ? exportName = null )
586
+ IEnumerable < IMethodSymbol > methods ,
587
+ string exportName )
586
588
{
587
- exportName ??= ToCamelCase ( method . Name ) ;
589
+ // TODO: Support exporting generic methods.
590
+ methods = methods . Where ( ( m ) => ! m . IsGenericMethod ) ;
591
+
592
+ IMethodSymbol ? method = methods . FirstOrDefault ( ) ;
593
+ if ( method == null )
594
+ {
595
+ return ;
596
+ }
588
597
589
- // An adapter method may be used to support marshalling arbitrary parameters,
590
- // if the method does not match the `JSCallback` signature.
591
598
string attributes = "JSPropertyAttributes.DefaultMethod" +
592
599
( method . IsStatic ? " | JSPropertyAttributes.Static" : string . Empty ) ;
593
- if ( method . IsGenericMethod )
600
+
601
+ if ( methods . Count ( ) == 1 && ! IsMethodCallbackAdapterRequired ( method ) )
594
602
{
595
- // TODO: Export generic method.
603
+ // No adapter is needed for a method with a JSCallback signature.
604
+ string ns = GetNamespace ( method ) ;
605
+ string className = method . ContainingType . Name ;
606
+ s += $ ".AddMethod(\" { exportName } \" , " +
607
+ $ "{ ns } .{ className } .{ method . Name } ,\n \t { attributes } )";
596
608
}
597
- else if ( IsMethodCallbackAdapterRequired ( method ) )
609
+ else if ( methods . Count ( ) == 1 )
598
610
{
611
+ // An adapter method supports marshalling arbitrary parameters.
599
612
Expression < JSCallback > adapter =
600
613
_marshaller . BuildFromJSMethodExpression ( method . AsMethodInfo ( ) ) ;
601
614
_callbackAdapters . Add ( adapter . Name ! , adapter ) ;
602
615
s += $ ".AddMethod(\" { exportName } \" , { adapter . Name } ,\n \t { attributes } )";
603
616
}
604
617
else
605
618
{
606
- string ns = GetNamespace ( method ) ;
607
- string className = method . ContainingType . Name ;
608
- s += $ ".AddMethod(\" { exportName } \" , " +
609
- $ "{ ns } .{ className } .{ method . Name } ,\n \t { attributes } )";
619
+ // An adapter method provides overload resolution.
620
+ LambdaExpression adapter = _marshaller . BuildMethodOverloadDescriptorExpression (
621
+ methods . Select ( ( m ) => m . AsMethodInfo ( ) ) . ToArray ( ) ) ;
622
+ _callbackAdapters . Add ( adapter . Name ! , adapter ) ;
623
+ s += $ ".AddMethod(\" { exportName } \" , { adapter . Name } (),\n \t { attributes } )";
610
624
}
611
625
}
612
626
@@ -616,10 +630,8 @@ private void ExportMethod(
616
630
private void ExportProperty (
617
631
ref SourceBuilder s ,
618
632
IPropertySymbol property ,
619
- string ? exportName = null )
633
+ string exportName )
620
634
{
621
- exportName ??= ToCamelCase ( property . Name ) ;
622
-
623
635
bool writable = property . SetMethod != null ||
624
636
( ! property . IsStatic && property . ContainingType . TypeKind == TypeKind . Struct ) ;
625
637
string attributes = "JSPropertyAttributes.Enumerable | JSPropertyAttributes.Configurable" +
0 commit comments