@@ -146,6 +146,7 @@ export class Program extends DiagnosticEmitter {
146146
147147 var queuedExports = new Map < string , QueuedExport > ( ) ;
148148 var queuedImports = new Array < QueuedImport > ( ) ;
149+ var queuedDerivedClasses = new Array < ClassPrototype > ( ) ;
149150
150151 // build initial lookup maps of internal names to declarations
151152 for ( var i = 0 , k = this . sources . length ; i < k ; ++ i ) {
@@ -156,7 +157,7 @@ export class Program extends DiagnosticEmitter {
156157 switch ( statement . kind ) {
157158
158159 case NodeKind . CLASS :
159- this . initializeClass ( < ClassDeclaration > statement ) ;
160+ this . initializeClass ( < ClassDeclaration > statement , queuedDerivedClasses ) ;
160161 break ;
161162
162163 case NodeKind . ENUM :
@@ -180,7 +181,7 @@ export class Program extends DiagnosticEmitter {
180181 break ;
181182
182183 case NodeKind . NAMESPACE :
183- this . initializeNamespace ( < NamespaceDeclaration > statement ) ;
184+ this . initializeNamespace ( < NamespaceDeclaration > statement , queuedDerivedClasses , null ) ;
184185 break ;
185186
186187 case NodeKind . TYPEDECLARATION :
@@ -232,6 +233,22 @@ export class Program extends DiagnosticEmitter {
232233 }
233234 } while ( currentExport ) ;
234235 }
236+
237+ // resolve base prototypes of derived classes
238+ for ( i = 0 , k = queuedDerivedClasses . length ; i < k ; ++ i ) {
239+ var derivedDeclaration = queuedDerivedClasses [ i ] . declaration ;
240+ assert ( derivedDeclaration != null ) ;
241+ var derivedType = ( < ClassDeclaration > derivedDeclaration ) . extendsType ;
242+ assert ( derivedType != null ) ;
243+ var resolved = this . resolveIdentifier ( ( < TypeNode > derivedType ) . identifier , null ) ; // reports
244+ if ( resolved ) {
245+ if ( resolved . element . kind != ElementKind . CLASS_PROTOTYPE ) {
246+ this . error ( DiagnosticCode . A_class_may_only_extend_another_class , ( < TypeNode > derivedType ) . range ) ;
247+ continue ;
248+ }
249+ queuedDerivedClasses [ i ] . basePrototype = < ClassPrototype > resolved . element ;
250+ }
251+ }
235252 }
236253
237254 /** Tries to resolve an import by traversing exports and queued exports. */
@@ -252,7 +269,7 @@ export class Program extends DiagnosticEmitter {
252269 } while ( true ) ;
253270 }
254271
255- private initializeClass ( declaration : ClassDeclaration , namespace : Element | null = null ) : void {
272+ private initializeClass ( declaration : ClassDeclaration , queuedDerivedClasses : ClassPrototype [ ] , namespace : Element | null = null ) : void {
256273 var internalName = declaration . internalName ;
257274 if ( this . elements . has ( internalName ) ) {
258275 this . error ( DiagnosticCode . Duplicate_identifier_0 , declaration . name . range , internalName ) ;
@@ -276,6 +293,10 @@ export class Program extends DiagnosticEmitter {
276293 } else if ( declaration . implementsTypes . length )
277294 throw new Error ( "not implemented" ) ;
278295
296+ // remember classes that extend another one
297+ if ( declaration . extendsType )
298+ queuedDerivedClasses . push ( prototype ) ;
299+
279300 // add as namespace member if applicable
280301 if ( namespace ) {
281302 if ( namespace . members ) {
@@ -762,7 +783,7 @@ export class Program extends DiagnosticEmitter {
762783 }
763784 }
764785
765- private initializeNamespace ( declaration : NamespaceDeclaration , parentNamespace : Element | null = null ) : void {
786+ private initializeNamespace ( declaration : NamespaceDeclaration , queuedExtendingClasses : ClassPrototype [ ] , parentNamespace : Element | null = null ) : void {
766787 var internalName = declaration . internalName ;
767788
768789 var namespace = this . elements . get ( internalName ) ;
@@ -794,7 +815,7 @@ export class Program extends DiagnosticEmitter {
794815 switch ( members [ i ] . kind ) {
795816
796817 case NodeKind . CLASS :
797- this . initializeClass ( < ClassDeclaration > members [ i ] , namespace ) ;
818+ this . initializeClass ( < ClassDeclaration > members [ i ] , queuedExtendingClasses , namespace ) ;
798819 break ;
799820
800821 case NodeKind . ENUM :
@@ -810,7 +831,7 @@ export class Program extends DiagnosticEmitter {
810831 break ;
811832
812833 case NodeKind . NAMESPACE :
813- this . initializeNamespace ( < NamespaceDeclaration > members [ i ] , namespace ) ;
834+ this . initializeNamespace ( < NamespaceDeclaration > members [ i ] , queuedExtendingClasses , namespace ) ;
814835 break ;
815836
816837 case NodeKind . TYPEDECLARATION :
@@ -957,22 +978,26 @@ export class Program extends DiagnosticEmitter {
957978 }
958979
959980 /** Resolves an identifier to the element it refers to. */
960- resolveIdentifier ( identifier : IdentifierExpression , contextualFunction : Function ) : ResolvedElement | null {
981+ resolveIdentifier ( identifier : IdentifierExpression , contextualFunction : Function | null ) : ResolvedElement | null {
961982 var name = identifier . name ;
962- var local = contextualFunction . locals . get ( name ) ;
963- if ( local )
964- return ( resolvedElement || ( resolvedElement = new ResolvedElement ( ) ) ) . set ( local ) ;
965983
966984 var element : Element | null ;
967985 var namespace : Element | null ;
968986
969- // search contextual parent namespaces if applicable
970- if ( contextualFunction && ( namespace = contextualFunction . prototype . namespace ) ) {
971- do {
972- if ( element = this . elements . get ( namespace . internalName + STATIC_DELIMITER + name ) )
973- // if ((namespace.members && (element = namespace.members.get(name))) || (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)))
974- return ( resolvedElement || ( resolvedElement = new ResolvedElement ( ) ) ) . set ( element ) ;
975- } while ( namespace = namespace . namespace ) ;
987+ if ( contextualFunction ) {
988+ // check locals
989+ var local = contextualFunction . locals . get ( name ) ;
990+ if ( local )
991+ return ( resolvedElement || ( resolvedElement = new ResolvedElement ( ) ) ) . set ( local ) ;
992+
993+ // search contextual parent namespaces if applicable
994+ if ( namespace = contextualFunction . prototype . namespace ) {
995+ do {
996+ if ( element = this . elements . get ( namespace . internalName + STATIC_DELIMITER + name ) )
997+ // if ((namespace.members && (element = namespace.members.get(name))) || (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)))
998+ return ( resolvedElement || ( resolvedElement = new ResolvedElement ( ) ) ) . set ( element ) ;
999+ } while ( namespace = namespace . namespace ) ;
1000+ }
9761001 }
9771002
9781003 // search current file
@@ -998,6 +1023,7 @@ export class Program extends DiagnosticEmitter {
9981023 // at this point we know exactly what the target is, so look up the element within
9991024 var propertyName = propertyAccess . property . name ;
10001025 var targetType : Type ;
1026+ var member : Element | null ;
10011027 switch ( target . kind ) {
10021028
10031029 case ElementKind . GLOBAL :
@@ -1008,12 +1034,31 @@ export class Program extends DiagnosticEmitter {
10081034 target = < Class > targetType . classType ;
10091035 // fall-through
10101036
1011- default :
1012- if ( target . members ) {
1013- var member = target . members . get ( propertyName ) ;
1014- if ( member )
1037+ case ElementKind . CLASS_PROTOTYPE :
1038+ case ElementKind . CLASS :
1039+ do {
1040+ if ( target . members && ( member = target . members . get ( propertyName ) ) )
10151041 return resolvedElement . set ( member ) . withTarget ( target , targetExpression ) ;
1016- }
1042+ // check inherited static members on the base prototype while target is a class prototype
1043+ if ( target . kind == ElementKind . CLASS_PROTOTYPE ) {
1044+ if ( ( < ClassPrototype > target ) . basePrototype )
1045+ target = < ClassPrototype > ( < ClassPrototype > target ) . basePrototype ;
1046+ else
1047+ break ;
1048+ // or inherited instance members on the cbase class while target is a class instance
1049+ } else if ( target . kind == ElementKind . CLASS ) {
1050+ if ( ( < Class > target ) . base )
1051+ target = < Class > ( < Class > target ) . base ;
1052+ else
1053+ break ;
1054+ } else
1055+ break ;
1056+ } while ( true ) ;
1057+ break ;
1058+
1059+ default : // enums or other namespace-like elements
1060+ if ( target . members && ( member = target . members . get ( propertyName ) ) )
1061+ return resolvedElement . set ( member ) . withTarget ( target , targetExpression ) ;
10171062 break ;
10181063 }
10191064 this . error ( DiagnosticCode . Property_0_does_not_exist_on_type_1 , propertyAccess . property . range , propertyName , target . internalName ) ;
@@ -1026,16 +1071,19 @@ export class Program extends DiagnosticEmitter {
10261071 if ( ! ( resolvedElement = this . resolveExpression ( targetExpression , contextualFunction ) ) )
10271072 return null ;
10281073 var target = resolvedElement . element ;
1029-
10301074 switch ( target . kind ) {
1075+
1076+ // TBD: should indexed access on static classes, like `Heap`, be a supported as well?
10311077 case ElementKind . CLASS :
10321078 var type = ( < Class > target ) . type ;
10331079 if ( type . classType ) {
1034- // TODO: check if array etc.
1080+ var indexedGet : FunctionPrototype | null ;
1081+ if ( indexedGet = ( target = type . classType ) . prototype . opIndexedGet )
1082+ return resolvedElement . set ( indexedGet ) . withTarget ( target , targetExpression ) ;
10351083 }
10361084 break ;
10371085 }
1038- this . error ( DiagnosticCode . Operation_not_supported , elementAccess . range ) ;
1086+ this . error ( DiagnosticCode . Index_signature_is_missing_in_type_0 , targetExpression . range , target . internalName ) ;
10391087 return null ;
10401088 }
10411089
@@ -1820,15 +1868,17 @@ export class ClassPrototype extends Element {
18201868 instances : Map < string , Class > = new Map ( ) ;
18211869 /** Instance member prototypes. */
18221870 instanceMembers : Map < string , Element > | null = null ;
1871+ /** Base class prototype, if applicable. */
1872+ basePrototype : ClassPrototype | null = null ; // set in Program#initialize
18231873
18241874 /** Overloaded indexed get method, if any. */
1825- opIndexedGet : FunctionPrototype | null ;
1875+ opIndexedGet : FunctionPrototype | null = null ; // TODO: indexedGet and indexedSet as an accessor?
18261876 /** Overloaded indexed set method, if any. */
1827- opIndexedSet : FunctionPrototype | null ;
1877+ opIndexedSet : FunctionPrototype | null = null ;
18281878 /** Overloaded concatenation method, if any. */
1829- opConcat : FunctionPrototype | null ;
1879+ opConcat : FunctionPrototype | null = null ;
18301880 /** Overloaded equality comparison method, if any. */
1831- opEquals : FunctionPrototype | null ;
1881+ opEquals : FunctionPrototype | null = null ;
18321882
18331883 constructor ( program : Program , simpleName : string , internalName : string , declaration : ClassDeclaration | null = null ) {
18341884 super ( program , simpleName , internalName ) ;
@@ -1878,16 +1928,6 @@ export class ClassPrototype extends Element {
18781928 this . program . error ( DiagnosticCode . A_class_may_only_extend_another_class , declaration . extendsType . range ) ;
18791929 return null ;
18801930 }
1881- if ( ( this . flags & ElementFlags . HAS_STATIC_BASE_MEMBERS ) == 0 ) { // inherit static base members once
1882- this . flags |= ElementFlags . HAS_STATIC_BASE_MEMBERS ;
1883- if ( baseClass . prototype . members ) {
1884- if ( ! this . members )
1885- this . members = new Map ( ) ;
1886- for ( var baseMember of baseClass . prototype . members . values ( ) )
1887- if ( ! baseMember . isInstance )
1888- this . members . set ( baseMember . simpleName , baseMember ) ;
1889- }
1890- }
18911931 if ( baseClass . prototype . isStruct != this . isStruct ) {
18921932 this . program . error ( DiagnosticCode . Structs_cannot_extend_classes_and_vice_versa , Range . join ( declaration . name . range , declaration . extendsType . range ) ) ;
18931933 return null ;
0 commit comments