@@ -2729,6 +2729,9 @@ export class Resolver extends DiagnosticEmitter {
2729
2729
) ;
2730
2730
}
2731
2731
2732
+ /** Currently resolving classes. */
2733
+ private resolveClassPending : Class [ ] = [ ] ;
2734
+
2732
2735
/** Resolves a class prototype using the specified concrete type arguments. */
2733
2736
resolveClass (
2734
2737
/** The prototype of the class. */
@@ -2742,10 +2745,19 @@ export class Resolver extends DiagnosticEmitter {
2742
2745
) : Class | null {
2743
2746
var instanceKey = typeArguments ? typesToString ( typeArguments ) : "" ;
2744
2747
2745
- // Check if this exact instance has already been resolved
2748
+ // Do not attempt to resolve the same class twice. This can return a class
2749
+ // that isn't fully resolved yet, but only on deeper levels of recursion.
2746
2750
var instance = prototype . getResolvedInstance ( instanceKey ) ;
2747
2751
if ( instance ) return instance ;
2748
2752
2753
+ // Otherwise create
2754
+ var nameInclTypeParamters = prototype . name ;
2755
+ if ( instanceKey . length ) nameInclTypeParamters += "<" + instanceKey + ">" ;
2756
+ instance = new Class ( nameInclTypeParamters , prototype , typeArguments ) ;
2757
+ prototype . setResolvedInstance ( instanceKey , instance ) ;
2758
+ var pendingClasses = this . resolveClassPending ;
2759
+ pendingClasses . push ( instance ) ;
2760
+
2749
2761
// Insert contextual type arguments for this operation. Internally, this method is always
2750
2762
// called with matching type parameter / argument counts.
2751
2763
if ( typeArguments ) {
@@ -2760,13 +2772,10 @@ export class Resolver extends DiagnosticEmitter {
2760
2772
let typeParameterNodes = prototype . typeParameterNodes ;
2761
2773
assert ( ! ( typeParameterNodes && typeParameterNodes . length ) ) ;
2762
2774
}
2763
-
2764
- // There shouldn't be an instance before resolving the base class
2765
- assert ( ! prototype . getResolvedInstance ( instanceKey ) ) ;
2775
+ instance . contextualTypeArguments = ctxTypes ;
2766
2776
2767
2777
// Resolve base class if applicable
2768
2778
var basePrototype = prototype . basePrototype ;
2769
- var baseClass : Class | null = null ;
2770
2779
if ( basePrototype ) {
2771
2780
let current : ClassPrototype | null = basePrototype ;
2772
2781
do {
@@ -2780,70 +2789,68 @@ export class Resolver extends DiagnosticEmitter {
2780
2789
}
2781
2790
} while ( current = current . basePrototype ) ;
2782
2791
let extendsNode = assert ( prototype . extendsNode ) ; // must be present if it has a base prototype
2783
- baseClass = this . resolveClassInclTypeArguments (
2792
+ let base = this . resolveClassInclTypeArguments (
2784
2793
basePrototype ,
2785
2794
extendsNode . typeArguments ,
2786
2795
prototype . parent , // relative to derived class
2787
2796
makeMap ( ctxTypes ) , // don't inherit
2788
2797
extendsNode ,
2789
2798
reportMode
2790
2799
) ;
2791
- if ( ! baseClass ) return null ;
2792
- }
2800
+ if ( ! base ) return null ;
2801
+ instance . setBase ( base ) ;
2793
2802
2794
- // Construct the instance and remember that it has been resolved already
2795
- var recursiveInstanceFromResolvingBase = prototype . getResolvedInstance ( instanceKey ) ;
2796
- if ( recursiveInstanceFromResolvingBase ) {
2797
- // Happens if the base class already triggers resolving this class
2798
- instance = recursiveInstanceFromResolvingBase ;
2799
- } else {
2800
- let nameInclTypeParamters = prototype . name ;
2801
- if ( instanceKey . length ) nameInclTypeParamters += "<" + instanceKey + ">" ;
2802
- instance = new Class ( nameInclTypeParamters , prototype , typeArguments , baseClass ) ;
2803
- prototype . setResolvedInstance ( instanceKey , instance ) ;
2803
+ // If the base class is still pending, yield here and instead resolve any
2804
+ // derived classes once the base class's `finishResolveClass` is done.
2805
+ // This is guaranteed to never happen at the entry of the recursion, i.e.
2806
+ // where `resolveClass` is called from other code.
2807
+ if ( pendingClasses . includes ( base ) ) return instance ;
2804
2808
}
2805
- instance . contextualTypeArguments = ctxTypes ; // unique (as specified by the caller)
2806
2809
2807
- // Inherit base class members and set up the initial memory offset for own fields
2810
+ // We only get here if the base class has been fully resolved already.
2811
+ this . finishResolveClass ( instance , reportMode ) ;
2812
+ return instance ;
2813
+ }
2814
+
2815
+ /** Finishes resolving the specified class. */
2816
+ private finishResolveClass (
2817
+ /** Class to finish resolving. */
2818
+ instance : Class ,
2819
+ /** How to proceed with eventual diagnostics. */
2820
+ reportMode : ReportMode
2821
+ ) : void {
2822
+ var instanceMembers = instance . members ;
2823
+ if ( ! instanceMembers ) instance . members = instanceMembers = new Map ( ) ;
2824
+
2825
+ // Alias base members
2826
+ var pendingClasses = this . resolveClassPending ;
2808
2827
var memoryOffset : u32 = 0 ;
2809
- if ( baseClass ) {
2810
- let baseMembers = baseClass . members ;
2828
+ var base = instance . base ;
2829
+ if ( base ) {
2830
+ assert ( ! pendingClasses . includes ( base ) ) ;
2831
+ let baseMembers = base . members ;
2811
2832
if ( baseMembers ) {
2812
- let instanceMembers = instance . members ;
2813
- if ( ! instanceMembers ) instance . members = instanceMembers = new Map ( ) ;
2814
2833
for ( let [ baseMemberName , baseMember ] of baseMembers ) {
2815
2834
instanceMembers . set ( baseMemberName , baseMember ) ;
2816
2835
}
2817
2836
}
2818
- memoryOffset = baseClass . currentMemoryOffset ;
2837
+ memoryOffset = base . nextMemoryOffset ;
2819
2838
}
2820
2839
2821
2840
// Resolve instance members
2841
+ var prototype = instance . prototype ;
2822
2842
var instanceMemberPrototypes = prototype . instanceMembers ;
2823
2843
if ( instanceMemberPrototypes ) {
2824
2844
for ( let member of instanceMemberPrototypes . values ( ) ) {
2825
2845
switch ( member . kind ) {
2826
2846
2827
- // Lay out fields in advance
2828
2847
case ElementKind . FIELD_PROTOTYPE : {
2829
- let instanceMembers = instance . members ;
2830
- if ( ! instanceMembers ) instance . members = instanceMembers = new Map ( ) ;
2831
- else if ( instanceMembers . has ( member . name ) ) {
2832
- let existing = instanceMembers . get ( member . name ) ! ;
2833
- this . errorRelated (
2834
- DiagnosticCode . Duplicate_identifier_0 ,
2835
- ( < FieldPrototype > member ) . identifierNode . range ,
2836
- existing . declaration . name . range ,
2837
- member . name
2838
- ) ;
2839
- break ;
2840
- }
2841
2848
let fieldTypeNode = ( < FieldPrototype > member ) . typeNode ;
2842
2849
let fieldType : Type | null = null ;
2843
2850
// TODO: handle duplicate non-private fields specifically?
2844
2851
if ( ! fieldTypeNode ) {
2845
- if ( baseClass ) {
2846
- let baseMembers = baseClass . members ;
2852
+ if ( base ) {
2853
+ let baseMembers = base . members ;
2847
2854
if ( baseMembers && baseMembers . has ( ( < FieldPrototype > member ) . name ) ) {
2848
2855
let baseField = baseMembers . get ( ( < FieldPrototype > member ) . name ) ! ;
2849
2856
if ( ! baseField . is ( CommonFlags . PRIVATE ) ) {
@@ -2869,13 +2876,13 @@ export class Resolver extends DiagnosticEmitter {
2869
2876
) ;
2870
2877
}
2871
2878
if ( ! fieldType ) break ; // did report above
2872
- let fieldInstance = new Field ( < FieldPrototype > member , instance , fieldType ) ;
2879
+ let field = new Field ( < FieldPrototype > member , instance , fieldType ) ;
2873
2880
assert ( isPowerOf2 ( fieldType . byteSize ) ) ;
2874
2881
let mask = fieldType . byteSize - 1 ;
2875
2882
if ( memoryOffset & mask ) memoryOffset = ( memoryOffset | mask ) + 1 ;
2876
- fieldInstance . memoryOffset = memoryOffset ;
2883
+ field . memoryOffset = memoryOffset ;
2877
2884
memoryOffset += fieldType . byteSize ;
2878
- instance . add ( member . name , fieldInstance ) ; // reports
2885
+ instance . add ( member . name , field ) ; // reports
2879
2886
break ;
2880
2887
}
2881
2888
case ElementKind . FUNCTION_PROTOTYPE : {
@@ -2923,7 +2930,7 @@ export class Resolver extends DiagnosticEmitter {
2923
2930
}
2924
2931
2925
2932
// Finalize memory offset
2926
- instance . currentMemoryOffset = memoryOffset ;
2933
+ instance . nextMemoryOffset = memoryOffset ;
2927
2934
2928
2935
// Link _own_ constructor if present
2929
2936
{
@@ -2933,7 +2940,7 @@ export class Resolver extends DiagnosticEmitter {
2933
2940
let ctorInstance = this . resolveFunction (
2934
2941
< FunctionPrototype > ctorPrototype ,
2935
2942
null ,
2936
- instance . contextualTypeArguments ,
2943
+ assert ( instance . contextualTypeArguments ) ,
2937
2944
reportMode
2938
2945
) ;
2939
2946
if ( ctorInstance ) instance . constructorInstance = < Function > ctorInstance ;
@@ -3002,7 +3009,24 @@ export class Resolver extends DiagnosticEmitter {
3002
3009
}
3003
3010
}
3004
3011
}
3005
- return instance ;
3012
+
3013
+ // Remove this class from pending
3014
+ var pendingIndex = pendingClasses . indexOf ( instance ) ;
3015
+ assert ( ~ pendingIndex ) ; // must be pending
3016
+ pendingClasses . splice ( pendingIndex , 1 ) ;
3017
+
3018
+ // Finish derived classes that we postponed in `resolveClass` due to the
3019
+ // base class still being pending, again triggering `finishResolveClass`
3020
+ // of any classes derived from those classes, ultimately leading to all
3021
+ // pending classes being resolved.
3022
+ var derivedPendingClasses = new Array < Class > ( ) ;
3023
+ for ( let i = 0 , k = pendingClasses . length ; i < k ; ++ i ) {
3024
+ let pending = pendingClasses [ i ] ;
3025
+ if ( instance == pending . base ) derivedPendingClasses . push ( pending ) ;
3026
+ }
3027
+ for ( let i = 0 , k = derivedPendingClasses . length ; i < k ; ++ i ) {
3028
+ this . finishResolveClass ( derivedPendingClasses [ i ] , reportMode ) ;
3029
+ }
3006
3030
}
3007
3031
3008
3032
/** Resolves a class prototype by first resolving the specified type arguments. */
0 commit comments