feat(decorators): adds support for parameter decorators.

Paramater decorators expect to be called as currently implemented by TS.
This commit is contained in:
Rado Kirov
2015-04-29 16:22:38 -07:00
parent e4342743c0
commit f863ea0db5
5 changed files with 182 additions and 17 deletions

View File

@ -5,12 +5,15 @@ import {
import {ViewAnnotation} from '../annotations/view';
import {AncestorAnnotation, ParentAnnotation} from '../annotations/visibility';
import {AttributeAnnotation, QueryAnnotation} from '../annotations/di';
import {global} from 'angular2/src/facade/lang';
function makeDecorator(annotationCls) {
return function(...args) {
if (!(window.Reflect && window.Reflect.getMetadata)) throw 'reflect-metadata shim is required';
var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}
var annotationInstance = new annotationCls(...args);
var Reflect = window.Reflect;
return function(cls) {
var annotations = Reflect.getMetadata('annotations', cls);
annotations = annotations || [];
@ -21,17 +24,39 @@ function makeDecorator(annotationCls) {
}
}
function makeParamDecorator(annotationCls) {
return function(...args) {
var Reflect = global.Reflect;
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using parameter decorators';
}
var annotationInstance = new annotationCls(...args);
return function(cls, unusedKey, index) {
var parameters = Reflect.getMetadata('parameters', cls);
parameters = parameters || [];
// there might be gaps if some in between parameters do not have annotations.
// we pad with nulls.
while (parameters.length <= index) {
parameters.push(null);
}
parameters[index] = annotationInstance;
Reflect.defineMetadata('parameters', parameters, cls);
return cls;
}
}
}
/* from annotations */
export var Component = makeDecorator(ComponentAnnotation);
export var Decorator = makeDecorator(DirectiveAnnotation);
/* from di */
export var Attribute = makeDecorator(AttributeAnnotation);
export var Query = makeDecorator(QueryAnnotation);
/* from view */
export var View = makeDecorator(ViewAnnotation);
/* from visiblity */
export var Ancestor = makeDecorator(AncestorAnnotation);
export var Parent = makeDecorator(ParentAnnotation);
/* from visibility */
export var Ancestor = makeParamDecorator(AncestorAnnotation);
export var Parent = makeParamDecorator(ParentAnnotation);
/* from di */
export var Attribute = makeParamDecorator(AttributeAnnotation);
export var Query = makeParamDecorator(QueryAnnotation);

View File

@ -217,3 +217,6 @@ class DateWrapper {
return date.toUtc().toIso8601String();
}
}
// needed to match the exports from lang.js
var global = null;

View File

@ -38,15 +38,35 @@ export class ReflectionCapabilities {
return typeOfFunc.parameters;
}
if (isPresent(window.Reflect) && isPresent(window.Reflect.getMetadata)) {
var paramtypes = window.Reflect.getMetadata('design:paramtypes', typeOfFunc);
if (isPresent(paramtypes)) {
// TODO(rado): add parameter annotations here.
return paramtypes.map((p) => [p]);
var paramAnnotations = window.Reflect.getMetadata('parameters', typeOfFunc);
var paramTypes = window.Reflect.getMetadata('design:paramtypes', typeOfFunc);
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
return this._zipTypesAndAnnotaions(paramTypes, paramAnnotations);
}
}
return ListWrapper.createFixedSize(typeOfFunc.length);
}
_zipTypesAndAnnotaions(paramTypes, paramAnnotations) {
var result = ListWrapper.createFixedSize(paramTypes.length);
for (var i = 0; i < result.length; i++) {
// TS outputs Object for parameters without types, while Traceur omits
// the annotations. For now we preserve the Traceur behavior to aid
// migration, but this can be revisited.
if (paramTypes[i] != Object) {
result[i] = [paramTypes[i]];
} else {
result[i] = [];
}
if (isPresent(paramAnnotations[i])) {
result[i] = result[i].concat(paramAnnotations[i]);
}
}
return result;
}
annotations(typeOfFunc):List {
// Prefer the direct API.
if (isPresent(typeOfFunc.annotations)) {

View File

@ -45,16 +45,34 @@ export class ReflectionCapabilities {
throw new Error("Factory cannot take more than 10 arguments");
}
_zipTypesAndAnnotaions(paramTypes, paramAnnotations): List<List<any>> {
var result = ListWrapper.createFixedSize(paramTypes.length);
for (var i = 0; i < result.length; i++) {
// TS outputs Object for parameters without types, while Traceur omits
// the annotations. For now we preserve the Traceur behavior to aid
// migration, but this can be revisited.
if (paramTypes[i] != Object) {
result[i] = [paramTypes[i]];
} else {
result[i] = [];
}
if (isPresent(paramAnnotations[i])) {
result[i] = result[i].concat(paramAnnotations[i]);
}
}
return result;
}
parameters(typeOfFunc): List<List<any>> {
// Prefer the direct API.
if (isPresent(typeOfFunc.parameters)) {
return typeOfFunc.parameters;
}
if (isPresent(global.Reflect) && isPresent(global.Reflect.getMetadata)) {
var paramtypes = global.Reflect.getMetadata('design:paramtypes', typeOfFunc);
if (isPresent(paramtypes)) {
// TODO(rado): add parameter annotations here.
return paramtypes.map((p) => [p]);
var paramAnnotations = global.Reflect.getMetadata('parameters', typeOfFunc);
var paramTypes = global.Reflect.getMetadata('design:paramtypes', typeOfFunc);
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
return this._zipTypesAndAnnotaions(paramTypes, paramAnnotations);
}
}
return ListWrapper.createFixedSize(typeOfFunc.length);