From 627282d2c86405debac8c452322b353ac47271b5 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Tue, 29 Nov 2016 12:02:50 -0800 Subject: [PATCH] fix(compiler): correctly evaluate references to static functions (#13133) --- .../integrationtest/src/module.ts | 4 +-- .../integrationtest/src/module_fixtures.ts | 22 +++++++--------- .../compiler/src/aot/static_reflector.ts | 18 +++++++------ .../test/aot/static_reflector_spec.ts | 25 +++++++++++++++++++ 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/modules/@angular/compiler-cli/integrationtest/src/module.ts b/modules/@angular/compiler-cli/integrationtest/src/module.ts index 9abec97280..a690eb686d 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/module.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/module.ts @@ -19,7 +19,7 @@ import {BasicComp} from './basic'; import {ComponentUsingThirdParty} from './comp_using_3rdp'; import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components'; import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features'; -import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomePipeInRootModule, SomeService, someLibModuleWithProviders} from './module_fixtures'; +import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibModule, SomePipeInRootModule, SomeService} from './module_fixtures'; import {CompWithNgContent, ProjectingComp} from './projection'; import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries'; @@ -52,7 +52,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive FormsModule, MdButtonModule, ModuleUsingCustomElements, - someLibModuleWithProviders(), + SomeLibModule.withProviders(), ThirdpartyModule, ], providers: [SomeService], diff --git a/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts b/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts index bebdf7a725..b38de7daf8 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/module_fixtures.ts @@ -63,17 +63,13 @@ export function provideValueWithEntryComponents(value: any) { entryComponents: [CompUsingLibModuleDirectiveAndPipe], }) export class SomeLibModule { -} - -// TODO(tbosch): Make this a static method in `SomeLibModule` once -// our static reflector supports it. -// See https://github.com/angular/angular/issues/10266. -export function someLibModuleWithProviders(): ModuleWithProviders { - return { - ngModule: SomeLibModule, - providers: [ - ServiceUsingLibModule, - provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}]) - ] - }; + static withProviders() { + return { + ngModule: SomeLibModule, + providers: [ + ServiceUsingLibModule, provideValueWithEntryComponents( + [{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}]) + ] + }; + } } diff --git a/modules/@angular/compiler/src/aot/static_reflector.ts b/modules/@angular/compiler/src/aot/static_reflector.ts index 7bd24cf144..051f20f931 100644 --- a/modules/@angular/compiler/src/aot/static_reflector.ts +++ b/modules/@angular/compiler/src/aot/static_reflector.ts @@ -593,22 +593,26 @@ export class StaticReflector implements ReflectorReader { if (indexTarget && isPrimitive(index)) return indexTarget[index]; return null; case 'select': + let selectContext = context; let selectTarget = simplify(expression['expression']); if (selectTarget instanceof StaticSymbol) { // Access to a static instance variable + const member: string = expression['member']; + const members = selectTarget.members ? + (selectTarget.members as string[]).concat(member) : + [member]; const declarationValue = resolveReferenceValue(selectTarget); + selectContext = + self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members); if (declarationValue && declarationValue.statics) { selectTarget = declarationValue.statics; } else { - const member: string = expression['member']; - const members = selectTarget.members ? - (selectTarget.members as string[]).concat(member) : - [member]; - return self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members); + return selectContext; } } - const member = simplify(expression['member']); - if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]); + const member = simplifyInContext(selectContext, expression['member'], depth + 1); + if (selectTarget && isPrimitive(member)) + return simplifyInContext(selectContext, selectTarget[member], depth + 1); return null; case 'reference': if (!expression.module) { diff --git a/modules/@angular/compiler/test/aot/static_reflector_spec.ts b/modules/@angular/compiler/test/aot/static_reflector_spec.ts index 50c73db506..35cf5d5190 100644 --- a/modules/@angular/compiler/test/aot/static_reflector_spec.ts +++ b/modules/@angular/compiler/test/aot/static_reflector_spec.ts @@ -450,6 +450,17 @@ describe('StaticReflector', () => { ]); }); + it('should be able to get metadata for a class with nested method calls', () => { + const annotations = reflector.annotations( + reflector.getStaticSymbol('/tmp/src/static-method-call.ts', 'MyFactoryComponent')); + expect(annotations.length).toBe(1); + expect(annotations[0].providers).toEqual({ + provide: 'c', + useFactory: + reflector.getStaticSymbol('/tmp/src/static-method.ts', 'AnotherModule', ['someFactory']) + }); + }); + it('should be able to get the metadata for a class calling a method with default parameters', () => { const annotations = reflector.annotations( @@ -1231,6 +1242,15 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = { static defaultsMethod(a, b = true, c = false) { return [a, b, c]; } + static withFactory() { + return { provide: 'c', useFactory: AnotherModule.someFactory }; + } + } + + export class AnotherModule { + static someFactory() { + return 'e'; + } } `, '/tmp/src/static-method-call.ts': ` @@ -1251,6 +1271,11 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = { providers: [MyModule.defaultsMethod('a')] }) export class MyDefaultsComponent { } + + @Component({ + providers: MyModule.withFactory() + }) + export class MyFactoryComponent { } `, '/tmp/src/static-field.ts': ` import {Injectable} from '@angular/core';