diff --git a/modules/@angular/core/src/reflection/reflection_capabilities.ts b/modules/@angular/core/src/reflection/reflection_capabilities.ts index f1a26cdc26..c3dba802c6 100644 --- a/modules/@angular/core/src/reflection/reflection_capabilities.ts +++ b/modules/@angular/core/src/reflection/reflection_capabilities.ts @@ -7,7 +7,7 @@ */ import {global, isPresent, stringify} from '../facade/lang'; -import {Type} from '../type'; +import {Type, isType} from '../type'; import {PlatformReflectionCapabilities} from './platform_reflection_capabilities'; import {GetterFn, MethodFn, SetterFn} from './types'; @@ -105,7 +105,10 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { parameters(type: Type): any[][] { // Note: only report metadata if we have at least one class decorator // to stay in sync with the static reflector. - const parentCtor = Object.getPrototypeOf(type.prototype).constructor; + if (!isType(type)) { + return []; + } + const parentCtor = getParentCtor(type); let parameters = this._ownParameters(type, parentCtor); if (!parameters && parentCtor !== Object) { parameters = this.parameters(parentCtor); @@ -135,7 +138,10 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { } annotations(typeOrFunc: Type): any[] { - const parentCtor = Object.getPrototypeOf(typeOrFunc.prototype).constructor; + if (!isType(typeOrFunc)) { + return []; + } + const parentCtor = getParentCtor(typeOrFunc); const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || []; const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : []; return parentAnnotations.concat(ownAnnotations); @@ -170,7 +176,10 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { } propMetadata(typeOrFunc: any): {[key: string]: any[]} { - const parentCtor = Object.getPrototypeOf(typeOrFunc.prototype).constructor; + if (!isType(typeOrFunc)) { + return {}; + } + const parentCtor = getParentCtor(typeOrFunc); const propMetadata: {[key: string]: any[]} = {}; if (parentCtor !== Object) { const parentPropMetadata = this.propMetadata(parentCtor); @@ -233,3 +242,11 @@ function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] return new annotationCls(...annotationArgs); }); } + +function getParentCtor(ctor: Function): Type { + const parentProto = Object.getPrototypeOf(ctor.prototype); + const parentCtor = parentProto ? parentProto.constructor : null; + // Note: We always use `Object` as the null value + // to simplify checking later on. + return parentCtor || Object; +} diff --git a/modules/@angular/core/src/type.ts b/modules/@angular/core/src/type.ts index 21e6e29fad..72524fd2a8 100644 --- a/modules/@angular/core/src/type.ts +++ b/modules/@angular/core/src/type.ts @@ -18,5 +18,8 @@ */ export const Type = Function; +export function isType(v: any): v is Type { + return typeof v === 'function'; +} export interface Type extends Function { new (...args: any[]): T; } diff --git a/modules/@angular/core/test/linker/regression_integration_spec.ts b/modules/@angular/core/test/linker/regression_integration_spec.ts index 1afb25cb43..fea9b6df76 100644 --- a/modules/@angular/core/test/linker/regression_integration_spec.ts +++ b/modules/@angular/core/test/linker/regression_integration_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Injector, OpaqueToken, Pipe, PipeTransform, Provider} from '@angular/core'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, Injector, OpaqueToken, Pipe, PipeTransform, Provider} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/matchers'; @@ -119,7 +119,7 @@ function declareTests({useJit}: {useJit: boolean}) { expect(injector.get(token)).toEqual(tokenValue); }); - it('should support providers with an anonymous function', () => { + it('should support providers with an anonymous function as token', () => { const token = () => true; const tokenValue = 1; const injector = createInjector([{provide: token, useValue: tokenValue}]); @@ -147,6 +147,22 @@ function declareTests({useJit}: {useJit: boolean}) { const injector = createInjector([{provide: 'someToken', useValue: data}]); expect(injector.get('someToken')).toEqual(data); }); + + describe('ANALYZE_FOR_ENTRY_COMPONENTS providers', () => { + + it('should support class instances', () => { + class SomeObject { + someMethod() {} + } + + expect( + () => createInjector([ + {provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: new SomeObject(), multi: true} + ])) + .not.toThrow(); + }); + }); + }); it('should allow logging a previous elements class binding via interpolation', () => { diff --git a/modules/@angular/core/test/reflection/reflector_spec.ts b/modules/@angular/core/test/reflection/reflector_spec.ts index de406a8af1..35aef6104b 100644 --- a/modules/@angular/core/test/reflection/reflector_spec.ts +++ b/modules/@angular/core/test/reflection/reflector_spec.ts @@ -190,6 +190,8 @@ export function main() { class ChildNoDecorators extends Parent {} + class NoDecorators {} + // Check that metadata for Parent was not changed! expect(reflector.annotations(Parent)).toEqual([new ClassDecorator({value: 'parent'})]); @@ -199,6 +201,11 @@ export function main() { expect(reflector.annotations(ChildNoDecorators)).toEqual([new ClassDecorator( {value: 'parent'})]); + + expect(reflector.annotations(NoDecorators)).toEqual([]); + expect(reflector.annotations({})).toEqual([]); + expect(reflector.annotations(1)).toEqual([]); + expect(reflector.annotations(null)).toEqual([]); }); it('should inherit parameters', () => { @@ -226,6 +233,8 @@ export function main() { constructor(a: any, b: any, c: any) { super(null, null); } } + class NoDecorators {} + // Check that metadata for Parent was not changed! expect(reflector.parameters(Parent)).toEqual([ [A, new ParamDecorator('a')], [B, new ParamDecorator('b')] @@ -242,6 +251,11 @@ export function main() { expect(reflector.parameters(ChildWithCtorNoDecorator)).toEqual([ undefined, undefined, undefined ]); + + expect(reflector.parameters(NoDecorators)).toEqual([]); + expect(reflector.parameters({})).toEqual([]); + expect(reflector.parameters(1)).toEqual([]); + expect(reflector.parameters(null)).toEqual([]); }); it('should inherit property metadata', () => { @@ -263,6 +277,8 @@ export function main() { c: C; } + class NoDecorators {} + // Check that metadata for Parent was not changed! expect(reflector.propMetadata(Parent)).toEqual({ 'a': [new PropDecorator('a')], @@ -274,6 +290,11 @@ export function main() { 'b': [new PropDecorator('b1'), new PropDecorator('b2')], 'c': [new PropDecorator('c')] }); + + expect(reflector.propMetadata(NoDecorators)).toEqual({}); + expect(reflector.propMetadata({})).toEqual({}); + expect(reflector.propMetadata(1)).toEqual({}); + expect(reflector.propMetadata(null)).toEqual({}); }); it('should inherit lifecycle hooks', () => {