diff --git a/packages/compiler/src/aot/static_reflector.ts b/packages/compiler/src/aot/static_reflector.ts index 3e9ce1de61..3c460c84db 100644 --- a/packages/compiler/src/aot/static_reflector.ts +++ b/packages/compiler/src/aot/static_reflector.ts @@ -444,7 +444,7 @@ export class StaticReflector implements CompileReflector { } else { const staticSymbol = expression; const declarationValue = resolveReferenceValue(staticSymbol); - if (declarationValue) { + if (declarationValue != null) { return simplifyInContext(staticSymbol, declarationValue, depth + 1, references); } else { return staticSymbol; @@ -522,8 +522,8 @@ export class StaticReflector implements CompileReflector { } return null; case 'index': - let indexTarget = simplify(expression['expression']); - let index = simplify(expression['index']); + let indexTarget = simplifyInContext(context, expression['expression'], depth, 0); + let index = simplifyInContext(context, expression['index'], depth, 0); if (indexTarget && isPrimitive(index)) return indexTarget[index]; return null; case 'select': @@ -535,7 +535,7 @@ export class StaticReflector implements CompileReflector { selectContext = self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members); const declarationValue = resolveReferenceValue(selectContext); - if (declarationValue) { + if (declarationValue != null) { return simplifyInContext( selectContext, declarationValue, depth + 1, references); } else { diff --git a/packages/compiler/test/aot/static_reflector_spec.ts b/packages/compiler/test/aot/static_reflector_spec.ts index 595d0f0031..f4f90ea174 100644 --- a/packages/compiler/test/aot/static_reflector_spec.ts +++ b/packages/compiler/test/aot/static_reflector_spec.ts @@ -914,6 +914,154 @@ describe('StaticReflector', () => { .toEqual([{data: {c: [3]}}]); }); + // Regression #18170 + it('should evaluate enums and statics that are 0', () => { + const data = Object.create(DEFAULT_TEST_DATA); + const file = '/tmp/src/my_component.ts'; + data[file] = ` + import {Component} from '@angular/core'; + import {provideRoutes} from './macro'; + import {MyEnum, MyClass} from './consts'; + + @Component({ + template: '
', + providers: [provideRoutes({ + path: 'foo', + data: { + e: MyEnum.Value + } + })] + }) + export class MyComponent { } + `; + data['/tmp/src/macro.ts'] = ` + import {ANALYZE_FOR_ENTRY_COMPONENTS, ROUTES} from '@angular/core'; + + export interface Route { + path?: string; + data?: any; + } + export type Routes = Route[]; + export function provideRoutes(routes: Routes): any { + return [ + {provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: routes}, + {provide: ROUTES, multi: true, useValue: routes}, + ]; + } + `; + data['/tmp/src/consts.ts'] = ` + export enum MyEnum { + Value = 0, + } + `; + init(data); + expect(reflector.annotations(reflector.getStaticSymbol(file, 'MyComponent'))[0] + .providers[0][0] + .useValue) + .toEqual({path: 'foo', data: {e: 0}}); + }); + + // Regression #18170 + it('should agressively evaluate enums selects', () => { + const data = Object.create(DEFAULT_TEST_DATA); + const file = '/tmp/src/my_component.ts'; + data[file] = ` + import {Component} from '@angular/core'; + import {provideRoutes} from './macro'; + import {E} from './indirect'; + + @Component({ + template: '', + providers: [provideRoutes({ + path: 'foo', + data: { + e: E.Value, + } + })] + }) + export class MyComponent { } + `; + data['/tmp/src/macro.ts'] = ` + import {ANALYZE_FOR_ENTRY_COMPONENTS, ROUTES} from '@angular/core'; + + export interface Route { + path?: string; + data?: any; + } + export type Routes = Route[]; + export function provideRoutes(routes: Routes): any { + return [ + {provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: routes}, + {provide: ROUTES, multi: true, useValue: routes}, + ]; + } + `; + data['/tmp/src/indirect.ts'] = ` + import {MyEnum} from './consts'; + + export const E = MyEnum; + `, + data['/tmp/src/consts.ts'] = ` + export enum MyEnum { + Value = 1, + } + `; + init(data); + expect(reflector.annotations(reflector.getStaticSymbol(file, 'MyComponent'))[0] + .providers[0][0] + .useValue) + .toEqual({path: 'foo', data: {e: 1}}); + }); + + // Regression #18170 + it('should agressively evaluate array indexes', () => { + const data = Object.create(DEFAULT_TEST_DATA); + const file = '/tmp/src/my_component.ts'; + data[file] = ` + import {Component} from '@angular/core'; + import {provideRoutes} from './macro'; + import {E} from './indirect'; + + @Component({ + template: '', + providers: [provideRoutes({ + path: 'foo', + data: { + e: E[E[E[1]]], + } + })] + }) + export class MyComponent { } + `; + data['/tmp/src/macro.ts'] = ` + import {ANALYZE_FOR_ENTRY_COMPONENTS, ROUTES} from '@angular/core'; + + export interface Route { + path?: string; + data?: any; + } + export type Routes = Route[]; + export function provideRoutes(routes: Routes): any { + return [ + {provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: routes}, + {provide: ROUTES, multi: true, useValue: routes}, + ]; + } + `; + data['/tmp/src/indirect.ts'] = ` + import {A} from './consts'; + + export const E = A; + `, + data['/tmp/src/consts.ts'] = ` + export const A = [0, 1]; + `; + init(data); + expect(reflector.annotations(reflector.getStaticSymbol(file, 'MyComponent'))[0] + .providers[0][0] + .useValue) + .toEqual({path: 'foo', data: {e: 1}}); + }); }); const DEFAULT_TEST_DATA: {[key: string]: any} = {