fix(compiler): correctly evaluate references to static functions (#13133)

This commit is contained in:
Tobias Bosch 2016-11-29 12:02:50 -08:00 committed by Victor Savkin
parent 2f7492c986
commit 627282d2c8
4 changed files with 47 additions and 22 deletions

View File

@ -19,7 +19,7 @@ import {BasicComp} from './basic';
import {ComponentUsingThirdParty} from './comp_using_3rdp'; import {ComponentUsingThirdParty} from './comp_using_3rdp';
import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components'; import {CompWithAnalyzeEntryComponentsProvider, CompWithEntryComponents} from './entry_components';
import {CompConsumingEvents, CompUsingPipes, CompWithProviders, CompWithReferences, DirPublishingEvents, ModuleUsingCustomElements} from './features'; 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 {CompWithNgContent, ProjectingComp} from './projection';
import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries'; import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, DirectiveForQuery} from './queries';
@ -52,7 +52,7 @@ import {CompForChildQuery, CompWithChildQuery, CompWithDirectiveChild, Directive
FormsModule, FormsModule,
MdButtonModule, MdButtonModule,
ModuleUsingCustomElements, ModuleUsingCustomElements,
someLibModuleWithProviders(), SomeLibModule.withProviders(),
ThirdpartyModule, ThirdpartyModule,
], ],
providers: [SomeService], providers: [SomeService],

View File

@ -63,17 +63,13 @@ export function provideValueWithEntryComponents(value: any) {
entryComponents: [CompUsingLibModuleDirectiveAndPipe], entryComponents: [CompUsingLibModuleDirectiveAndPipe],
}) })
export class SomeLibModule { export class SomeLibModule {
} static withProviders() {
return {
// TODO(tbosch): Make this a static method in `SomeLibModule` once ngModule: SomeLibModule,
// our static reflector supports it. providers: [
// See https://github.com/angular/angular/issues/10266. ServiceUsingLibModule, provideValueWithEntryComponents(
export function someLibModuleWithProviders(): ModuleWithProviders { [{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
return { ]
ngModule: SomeLibModule, };
providers: [ }
ServiceUsingLibModule,
provideValueWithEntryComponents([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
]
};
} }

View File

@ -593,22 +593,26 @@ export class StaticReflector implements ReflectorReader {
if (indexTarget && isPrimitive(index)) return indexTarget[index]; if (indexTarget && isPrimitive(index)) return indexTarget[index];
return null; return null;
case 'select': case 'select':
let selectContext = context;
let selectTarget = simplify(expression['expression']); let selectTarget = simplify(expression['expression']);
if (selectTarget instanceof StaticSymbol) { if (selectTarget instanceof StaticSymbol) {
// Access to a static instance variable // 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); const declarationValue = resolveReferenceValue(selectTarget);
selectContext =
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
if (declarationValue && declarationValue.statics) { if (declarationValue && declarationValue.statics) {
selectTarget = declarationValue.statics; selectTarget = declarationValue.statics;
} else { } else {
const member: string = expression['member']; return selectContext;
const members = selectTarget.members ?
(selectTarget.members as string[]).concat(member) :
[member];
return self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
} }
} }
const member = simplify(expression['member']); const member = simplifyInContext(selectContext, expression['member'], depth + 1);
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]); if (selectTarget && isPrimitive(member))
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
return null; return null;
case 'reference': case 'reference':
if (!expression.module) { if (!expression.module) {

View File

@ -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', it('should be able to get the metadata for a class calling a method with default parameters',
() => { () => {
const annotations = reflector.annotations( const annotations = reflector.annotations(
@ -1231,6 +1242,15 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
static defaultsMethod(a, b = true, c = false) { static defaultsMethod(a, b = true, c = false) {
return [a, b, c]; 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': ` '/tmp/src/static-method-call.ts': `
@ -1251,6 +1271,11 @@ const DEFAULT_TEST_DATA: {[key: string]: any} = {
providers: [MyModule.defaultsMethod('a')] providers: [MyModule.defaultsMethod('a')]
}) })
export class MyDefaultsComponent { } export class MyDefaultsComponent { }
@Component({
providers: MyModule.withFactory()
})
export class MyFactoryComponent { }
`, `,
'/tmp/src/static-field.ts': ` '/tmp/src/static-field.ts': `
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';