Skip to content

Commit a80ac0a

Browse files
tboschchuckjaz
authored andcommitted
fix(core): make decorators closure safe (#16905)
This is required as e.g. `token` from `@Inject` is accessed in string form via makeParamDecorator but as a property in the `ReflectiveInjector`. Closes #16889 as this is a more general fix.
1 parent 5af143e commit a80ac0a

File tree

9 files changed

+77
-123
lines changed

9 files changed

+77
-123
lines changed

packages/compiler/test/directive_resolver_spec.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,15 @@ export function main() {
114114

115115
it('should read out the Directive metadata', () => {
116116
const directiveMetadata = resolver.resolve(SomeDirective);
117-
expect(directiveMetadata)
118-
.toEqual(new Directive(
119-
{selector: 'someDirective', inputs: [], outputs: [], host: {}, queries: {}}));
117+
expect(directiveMetadata).toEqual(new Directive({
118+
selector: 'someDirective',
119+
inputs: [],
120+
outputs: [],
121+
host: {},
122+
queries: {},
123+
exportAs: undefined,
124+
providers: undefined
125+
}));
120126
});
121127

122128
it('should throw if not matching metadata is found', () => {
@@ -136,11 +142,25 @@ export function main() {
136142
class ChildWithDecorator extends Parent {
137143
}
138144

139-
expect(resolver.resolve(ChildNoDecorator))
140-
.toEqual(new Directive({selector: 'p', inputs: [], outputs: [], host: {}, queries: {}}));
141-
142-
expect(resolver.resolve(ChildWithDecorator))
143-
.toEqual(new Directive({selector: 'c', inputs: [], outputs: [], host: {}, queries: {}}));
145+
expect(resolver.resolve(ChildNoDecorator)).toEqual(new Directive({
146+
selector: 'p',
147+
inputs: [],
148+
outputs: [],
149+
host: {},
150+
queries: {},
151+
exportAs: undefined,
152+
providers: undefined
153+
}));
154+
155+
expect(resolver.resolve(ChildWithDecorator)).toEqual(new Directive({
156+
selector: 'c',
157+
inputs: [],
158+
outputs: [],
159+
host: {},
160+
queries: {},
161+
exportAs: undefined,
162+
providers: undefined
163+
}));
144164
});
145165

146166
describe('inputs', () => {

packages/core/src/di/metadata.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export interface Inject { token: any; }
5858
* @stable
5959
* @Annotation
6060
*/
61-
export const Inject: InjectDecorator = makeParamDecorator('Inject', [['token', undefined]]);
61+
export const Inject: InjectDecorator = makeParamDecorator('Inject', (token: any) => ({token}));
6262

6363

6464
/**
@@ -104,7 +104,7 @@ export interface Optional {}
104104
* @stable
105105
* @Annotation
106106
*/
107-
export const Optional: OptionalDecorator = makeParamDecorator('Optional', []);
107+
export const Optional: OptionalDecorator = makeParamDecorator('Optional');
108108

109109
/**
110110
* Type of the Injectable decorator / constructor function.
@@ -151,7 +151,7 @@ export interface Injectable {}
151151
* @stable
152152
* @Annotation
153153
*/
154-
export const Injectable: InjectableDecorator = <InjectableDecorator>makeDecorator('Injectable', []);
154+
export const Injectable: InjectableDecorator = <InjectableDecorator>makeDecorator('Injectable');
155155

156156
/**
157157
* Type of the Self decorator / constructor function.
@@ -195,7 +195,7 @@ export interface Self {}
195195
* @stable
196196
* @Annotation
197197
*/
198-
export const Self: SelfDecorator = makeParamDecorator('Self', []);
198+
export const Self: SelfDecorator = makeParamDecorator('Self');
199199

200200

201201
/**
@@ -240,7 +240,7 @@ export interface SkipSelf {}
240240
* @stable
241241
* @Annotation
242242
*/
243-
export const SkipSelf: SkipSelfDecorator = makeParamDecorator('SkipSelf', []);
243+
export const SkipSelf: SkipSelfDecorator = makeParamDecorator('SkipSelf');
244244

245245
/**
246246
* Type of the Host decorator / constructor function.
@@ -285,4 +285,4 @@ export interface Host {}
285285
* @stable
286286
* @Annotation
287287
*/
288-
export const Host: HostDecorator = makeParamDecorator('Host', []);
288+
export const Host: HostDecorator = makeParamDecorator('Host');

packages/core/src/di/reflective_provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ function _extractToken(
225225

226226
if (!Array.isArray(metadata)) {
227227
if (metadata instanceof Inject) {
228-
return _createDependency(metadata['token'], optional, null);
228+
return _createDependency(metadata.token, optional, null);
229229
} else {
230230
return _createDependency(metadata, optional, null);
231231
}
@@ -240,7 +240,7 @@ function _extractToken(
240240
token = paramMetadata;
241241

242242
} else if (paramMetadata instanceof Inject) {
243-
token = paramMetadata['token'];
243+
token = paramMetadata.token;
244244

245245
} else if (paramMetadata instanceof Optional) {
246246
optional = true;

packages/core/src/metadata/di.ts

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export interface Attribute { attributeName?: string; }
119119
* @Annotation
120120
*/
121121
export const Attribute: AttributeDecorator =
122-
makeParamDecorator('Attribute', [['attributeName', undefined]]);
122+
makeParamDecorator('Attribute', (attributeName?: string) => ({attributeName}));
123123

124124
/**
125125
* Type of the Query metadata.
@@ -207,14 +207,8 @@ export type ContentChildren = Query;
207207
export const ContentChildren: ContentChildrenDecorator =
208208
<ContentChildrenDecorator>makePropDecorator(
209209
'ContentChildren',
210-
[
211-
['selector', undefined], {
212-
first: false,
213-
isViewQuery: false,
214-
descendants: false,
215-
read: undefined,
216-
}
217-
],
210+
(selector?: any, data: any = {}) =>
211+
({selector, first: false, isViewQuery: false, descendants: false, ...data}),
218212
Query);
219213

220214
/**
@@ -273,15 +267,8 @@ export type ContentChild = Query;
273267
* @Annotation
274268
*/
275269
export const ContentChild: ContentChildDecorator = makePropDecorator(
276-
'ContentChild',
277-
[
278-
['selector', undefined], {
279-
first: true,
280-
isViewQuery: false,
281-
descendants: true,
282-
read: undefined,
283-
}
284-
],
270+
'ContentChild', (selector?: any, data: any = {}) =>
271+
({selector, first: true, isViewQuery: false, descendants: true, ...data}),
285272
Query);
286273

287274
/**
@@ -339,15 +326,8 @@ export type ViewChildren = Query;
339326
* @Annotation
340327
*/
341328
export const ViewChildren: ViewChildrenDecorator = makePropDecorator(
342-
'ViewChildren',
343-
[
344-
['selector', undefined], {
345-
first: false,
346-
isViewQuery: true,
347-
descendants: true,
348-
read: undefined,
349-
}
350-
],
329+
'ViewChildren', (selector?: any, data: any = {}) =>
330+
({selector, first: false, isViewQuery: true, descendants: true, ...data}),
351331
Query);
352332

353333
/**
@@ -403,13 +383,6 @@ export type ViewChild = Query;
403383
* @Annotation
404384
*/
405385
export const ViewChild: ViewChildDecorator = makePropDecorator(
406-
'ViewChild',
407-
[
408-
['selector', undefined], {
409-
first: true,
410-
isViewQuery: true,
411-
descendants: true,
412-
read: undefined,
413-
}
414-
],
386+
'ViewChild', (selector: any, data: any) =>
387+
({selector, first: true, isViewQuery: true, descendants: true, ...data}),
415388
Query);

packages/core/src/metadata/directives.ts

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -399,15 +399,8 @@ export interface Directive {
399399
* @stable
400400
* @Annotation
401401
*/
402-
export const Directive: DirectiveDecorator = <DirectiveDecorator>makeDecorator('Directive', {
403-
selector: undefined,
404-
inputs: undefined,
405-
outputs: undefined,
406-
host: undefined,
407-
providers: undefined,
408-
exportAs: undefined,
409-
queries: undefined
410-
});
402+
export const Directive: DirectiveDecorator =
403+
<DirectiveDecorator>makeDecorator('Directive', (dir: Directive = {}) => dir);
411404

412405
/**
413406
* Type of the Component decorator / constructor function.
@@ -691,26 +684,7 @@ export interface Component extends Directive {
691684
* @Annotation
692685
*/
693686
export const Component: ComponentDecorator = <ComponentDecorator>makeDecorator(
694-
'Component', {
695-
selector: undefined,
696-
inputs: undefined,
697-
outputs: undefined,
698-
host: undefined,
699-
exportAs: undefined,
700-
moduleId: undefined,
701-
providers: undefined,
702-
viewProviders: undefined,
703-
changeDetection: ChangeDetectionStrategy.Default,
704-
queries: undefined,
705-
templateUrl: undefined,
706-
template: undefined,
707-
styleUrls: undefined,
708-
styles: undefined,
709-
animations: undefined,
710-
encapsulation: undefined,
711-
interpolation: undefined,
712-
entryComponents: undefined
713-
},
687+
'Component', (c: Component = {}) => ({changeDetection: ChangeDetectionStrategy.Default, ...c}),
714688
Directive);
715689

716690
/**
@@ -750,10 +724,8 @@ export interface Pipe {
750724
* @stable
751725
* @Annotation
752726
*/
753-
export const Pipe: PipeDecorator = <PipeDecorator>makeDecorator('Pipe', {
754-
name: undefined,
755-
pure: true,
756-
});
727+
export const Pipe: PipeDecorator =
728+
<PipeDecorator>makeDecorator('Pipe', (p: Pipe) => ({pure: true, ...p}));
757729

758730

759731
/**
@@ -825,7 +797,7 @@ export interface Input {
825797
* @Annotation
826798
*/
827799
export const Input: InputDecorator =
828-
makePropDecorator('Input', [['bindingPropertyName', undefined]]);
800+
makePropDecorator('Input', (bindingPropertyName?: string) => ({bindingPropertyName}));
829801

830802
/**
831803
* Type of the Output decorator / constructor function.
@@ -891,7 +863,7 @@ export interface Output { bindingPropertyName?: string; }
891863
* @Annotation
892864
*/
893865
export const Output: OutputDecorator =
894-
makePropDecorator('Output', [['bindingPropertyName', undefined]]);
866+
makePropDecorator('Output', (bindingPropertyName?: string) => ({bindingPropertyName}));
895867

896868

897869
/**
@@ -951,7 +923,7 @@ export interface HostBinding { hostPropertyName?: string; }
951923
* @Annotation
952924
*/
953925
export const HostBinding: HostBindingDecorator =
954-
makePropDecorator('HostBinding', [['hostPropertyName', undefined]]);
926+
makePropDecorator('HostBinding', (hostPropertyName?: string) => ({hostPropertyName}));
955927

956928

957929
/**
@@ -1013,4 +985,4 @@ export interface HostListener {
1013985
* @Annotation
1014986
*/
1015987
export const HostListener: HostListenerDecorator =
1016-
makePropDecorator('HostListener', [['eventName', undefined], ['args', []]]);
988+
makePropDecorator('HostListener', (eventName?: string, args?: string[]) => ({eventName, args}));

packages/core/src/metadata/ng_module.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,5 @@ export interface NgModule {
190190
* @stable
191191
* @Annotation
192192
*/
193-
export const NgModule: NgModuleDecorator = <NgModuleDecorator>makeDecorator('NgModule', {
194-
providers: undefined,
195-
declarations: undefined,
196-
imports: undefined,
197-
exports: undefined,
198-
entryComponents: undefined,
199-
bootstrap: undefined,
200-
schemas: undefined,
201-
id: undefined,
202-
});
193+
export const NgModule: NgModuleDecorator =
194+
<NgModuleDecorator>makeDecorator('NgModule', (ngModule: NgModule) => ngModule);

packages/core/src/util/decorators.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ export function Class(clsDef: ClassDefinition): Type<any> {
262262
* @suppress {globalThis}
263263
*/
264264
export function makeDecorator(
265-
name: string, props: {[name: string]: any}, parentClass?: any,
265+
name: string, props?: (...args: any[]) => any, parentClass?: any,
266266
chainFn?: (fn: Function) => void): (...args: any[]) => (cls: any) => any {
267-
const metaCtor = makeMetadataCtor([props]);
267+
const metaCtor = makeMetadataCtor(props);
268268

269269
function DecoratorFactory(objOrType: any): (cls: any) => any {
270270
if (!(Reflect && Reflect.getOwnMetadata)) {
@@ -301,25 +301,19 @@ export function makeDecorator(
301301
return DecoratorFactory;
302302
}
303303

304-
function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any {
304+
function makeMetadataCtor(props?: (...args: any[]) => any): any {
305305
return function ctor(...args: any[]) {
306-
props.forEach((prop, i) => {
307-
const argVal = args[i];
308-
if (Array.isArray(prop)) {
309-
// plain parameter
310-
this[prop[0]] = argVal === undefined ? prop[1] : argVal;
311-
} else {
312-
for (const propName in prop) {
313-
this[propName] =
314-
argVal && argVal.hasOwnProperty(propName) ? argVal[propName] : prop[propName];
315-
}
306+
if (props) {
307+
const values = props(...args);
308+
for (const propName in values) {
309+
this[propName] = values[propName];
316310
}
317-
});
311+
}
318312
};
319313
}
320314

321315
export function makeParamDecorator(
322-
name: string, props: ([string, any] | {[name: string]: any})[], parentClass?: any): any {
316+
name: string, props?: (...args: any[]) => any, parentClass?: any): any {
323317
const metaCtor = makeMetadataCtor(props);
324318
function ParamDecoratorFactory(...args: any[]): any {
325319
if (this instanceof ParamDecoratorFactory) {
@@ -356,7 +350,7 @@ export function makeParamDecorator(
356350
}
357351

358352
export function makePropDecorator(
359-
name: string, props: ([string, any] | {[key: string]: any})[], parentClass?: any): any {
353+
name: string, props?: (...args: any[]) => any, parentClass?: any): any {
360354
const metaCtor = makeMetadataCtor(props);
361355

362356
function PropDecoratorFactory(...args: any[]): any {

packages/core/test/reflection/reflector_spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ interface PropDecorator {
2929
}
3030

3131
/** @Annotation */ const ClassDecorator =
32-
<ClassDecoratorFactory>makeDecorator('ClassDecorator', {value: undefined});
32+
<ClassDecoratorFactory>makeDecorator('ClassDecorator', (data: any) => data);
3333
/** @Annotation */ const ParamDecorator =
34-
makeParamDecorator('ParamDecorator', [['value', undefined]]);
35-
/** @Annotation */ const PropDecorator = makePropDecorator('PropDecorator', [['value', undefined]]);
34+
makeParamDecorator('ParamDecorator', (value: any) => ({value}));
35+
/** @Annotation */ const PropDecorator =
36+
makePropDecorator('PropDecorator', (value: any) => ({value}));
3637

3738
class AType {
3839
constructor(public value: any) {}

0 commit comments

Comments
 (0)