2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2016-12-27 19:58:52 -05:00
|
|
|
import {Type, isType} from '../type';
|
2017-03-02 12:37:01 -05:00
|
|
|
import {global, stringify} from '../util';
|
2015-10-01 22:49:45 -04:00
|
|
|
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
2016-06-08 19:38:52 -04:00
|
|
|
import {GetterFn, MethodFn, SetterFn} from './types';
|
2015-04-24 16:17:36 -04:00
|
|
|
|
2016-11-18 18:17:44 -05:00
|
|
|
/**
|
|
|
|
* Attention: This regex has to hold even if the code is minified!
|
|
|
|
*/
|
|
|
|
export const DELEGATE_CTOR =
|
2017-01-24 12:05:34 -05:00
|
|
|
/^function\s+\S+\(\)\s*{\s*("use strict";)?\s*(return\s+)?(\S+\s+!==\s+null\s+&&\s+)?\S+\.apply\(this,\s*arguments\)/;
|
2016-11-18 18:17:44 -05:00
|
|
|
|
2015-05-18 20:19:54 -04:00
|
|
|
export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
2015-05-01 17:05:19 -04:00
|
|
|
private _reflect: any;
|
|
|
|
|
2017-03-01 18:18:10 -05:00
|
|
|
constructor(reflect?: any) { this._reflect = reflect || global['Reflect']; }
|
2015-05-01 17:05:19 -04:00
|
|
|
|
2015-07-10 11:54:53 -04:00
|
|
|
isReflectionEnabled(): boolean { return true; }
|
|
|
|
|
2016-10-06 18:11:16 -04:00
|
|
|
factory<T>(t: Type<T>): (args: any[]) => T { return (...args: any[]) => new t(...args); }
|
2015-04-24 16:17:36 -04:00
|
|
|
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2016-10-06 18:11:16 -04:00
|
|
|
_zipTypesAndAnnotations(paramTypes: any[], paramAnnotations: any[]): any[][] {
|
2016-11-12 08:08:58 -05:00
|
|
|
let result: any[][];
|
2015-05-18 02:11:13 -04:00
|
|
|
|
|
|
|
if (typeof paramTypes === 'undefined') {
|
2015-10-09 12:07:58 -04:00
|
|
|
result = new Array(paramAnnotations.length);
|
2015-05-18 02:11:13 -04:00
|
|
|
} else {
|
2015-10-09 12:07:58 -04:00
|
|
|
result = new Array(paramTypes.length);
|
2015-05-18 02:11:13 -04:00
|
|
|
}
|
|
|
|
|
2016-11-12 08:08:58 -05:00
|
|
|
for (let i = 0; i < result.length; i++) {
|
2015-04-29 19:22:38 -04:00
|
|
|
// 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.
|
2015-05-18 02:11:13 -04:00
|
|
|
if (typeof paramTypes === 'undefined') {
|
|
|
|
result[i] = [];
|
|
|
|
} else if (paramTypes[i] != Object) {
|
2015-04-29 19:22:38 -04:00
|
|
|
result[i] = [paramTypes[i]];
|
|
|
|
} else {
|
|
|
|
result[i] = [];
|
|
|
|
}
|
2017-03-02 12:37:01 -05:00
|
|
|
if (paramAnnotations && paramAnnotations[i] != null) {
|
2015-04-29 19:22:38 -04:00
|
|
|
result[i] = result[i].concat(paramAnnotations[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-18 18:17:44 -05:00
|
|
|
private _ownParameters(type: Type<any>, parentCtor: any): any[][] {
|
|
|
|
// If we have no decorators, we only have function.length as metadata.
|
|
|
|
// In that case, to detect whether a child class declared an own constructor or not,
|
|
|
|
// we need to look inside of that constructor to check whether it is
|
|
|
|
// just calling the parent.
|
|
|
|
// This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
|
|
|
|
// that sets 'design:paramtypes' to []
|
|
|
|
// if a class inherits from another class but has no ctor declared itself.
|
|
|
|
if (DELEGATE_CTOR.exec(type.toString())) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2015-04-28 21:17:00 -04:00
|
|
|
// Prefer the direct API.
|
2016-11-18 18:17:44 -05:00
|
|
|
if ((<any>type).parameters && (<any>type).parameters !== parentCtor.parameters) {
|
2016-10-06 18:11:16 -04:00
|
|
|
return (<any>type).parameters;
|
2015-04-28 21:17:00 -04:00
|
|
|
}
|
2016-05-02 01:47:25 -04:00
|
|
|
|
|
|
|
// API of tsickle for lowering decorators to properties on the class.
|
2016-11-15 12:19:00 -05:00
|
|
|
const tsickleCtorParams = (<any>type).ctorParameters;
|
2016-11-18 18:17:44 -05:00
|
|
|
if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) {
|
2016-11-15 12:19:00 -05:00
|
|
|
// Newer tsickle uses a function closure
|
|
|
|
// Retain the non-function case for compatibility with older tsickle
|
|
|
|
const ctorParameters =
|
|
|
|
typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams;
|
2016-10-06 18:11:16 -04:00
|
|
|
const paramTypes = ctorParameters.map((ctorParam: any) => ctorParam && ctorParam.type);
|
|
|
|
const paramAnnotations = ctorParameters.map(
|
|
|
|
(ctorParam: any) =>
|
2016-06-08 19:38:52 -04:00
|
|
|
ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
|
2016-05-02 01:47:25 -04:00
|
|
|
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
|
|
|
}
|
|
|
|
|
|
|
|
// API for metadata created by invoking the decorators.
|
2017-03-02 12:37:01 -05:00
|
|
|
if (this._reflect != null && this._reflect.getOwnMetadata != null) {
|
2016-11-18 18:17:44 -05:00
|
|
|
const paramAnnotations = this._reflect.getOwnMetadata('parameters', type);
|
|
|
|
const paramTypes = this._reflect.getOwnMetadata('design:paramtypes', type);
|
2016-10-06 18:11:16 -04:00
|
|
|
if (paramTypes || paramAnnotations) {
|
2016-01-17 03:46:15 -05:00
|
|
|
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
2015-04-28 21:17:00 -04:00
|
|
|
}
|
|
|
|
}
|
2016-11-18 18:17:44 -05:00
|
|
|
|
|
|
|
// If a class has no decorators, at least create metadata
|
|
|
|
// based on function.length.
|
|
|
|
// Note: We know that this is a real constructor as we checked
|
|
|
|
// the content of the constructor above.
|
2016-10-06 18:11:16 -04:00
|
|
|
return new Array((<any>type.length)).fill(undefined);
|
2015-04-24 16:17:36 -04:00
|
|
|
}
|
|
|
|
|
2016-11-18 18:17:44 -05:00
|
|
|
parameters(type: Type<any>): any[][] {
|
|
|
|
// Note: only report metadata if we have at least one class decorator
|
|
|
|
// to stay in sync with the static reflector.
|
2016-12-27 19:58:52 -05:00
|
|
|
if (!isType(type)) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
const parentCtor = getParentCtor(type);
|
2016-11-18 18:17:44 -05:00
|
|
|
let parameters = this._ownParameters(type, parentCtor);
|
|
|
|
if (!parameters && parentCtor !== Object) {
|
|
|
|
parameters = this.parameters(parentCtor);
|
|
|
|
}
|
|
|
|
return parameters || [];
|
|
|
|
}
|
|
|
|
|
|
|
|
private _ownAnnotations(typeOrFunc: Type<any>, parentCtor: any): any[] {
|
2015-04-28 21:17:00 -04:00
|
|
|
// Prefer the direct API.
|
2016-11-18 18:17:44 -05:00
|
|
|
if ((<any>typeOrFunc).annotations && (<any>typeOrFunc).annotations !== parentCtor.annotations) {
|
2016-10-06 18:11:16 -04:00
|
|
|
let annotations = (<any>typeOrFunc).annotations;
|
2016-10-19 16:42:39 -04:00
|
|
|
if (typeof annotations === 'function' && annotations.annotations) {
|
2015-06-16 19:13:31 -04:00
|
|
|
annotations = annotations.annotations;
|
|
|
|
}
|
|
|
|
return annotations;
|
2015-04-28 21:17:00 -04:00
|
|
|
}
|
2016-05-02 01:47:25 -04:00
|
|
|
|
|
|
|
// API of tsickle for lowering decorators to properties on the class.
|
2016-11-18 18:17:44 -05:00
|
|
|
if ((<any>typeOrFunc).decorators && (<any>typeOrFunc).decorators !== parentCtor.decorators) {
|
2016-05-02 01:47:25 -04:00
|
|
|
return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators);
|
|
|
|
}
|
|
|
|
|
|
|
|
// API for metadata created by invoking the decorators.
|
2016-11-18 18:17:44 -05:00
|
|
|
if (this._reflect && this._reflect.getOwnMetadata) {
|
|
|
|
return this._reflect.getOwnMetadata('annotations', typeOrFunc);
|
2015-04-28 21:17:00 -04:00
|
|
|
}
|
2015-04-24 16:17:36 -04:00
|
|
|
}
|
|
|
|
|
2016-11-18 18:17:44 -05:00
|
|
|
annotations(typeOrFunc: Type<any>): any[] {
|
2016-12-27 19:58:52 -05:00
|
|
|
if (!isType(typeOrFunc)) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
const parentCtor = getParentCtor(typeOrFunc);
|
2016-11-18 18:17:44 -05:00
|
|
|
const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || [];
|
|
|
|
const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : [];
|
|
|
|
return parentAnnotations.concat(ownAnnotations);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _ownPropMetadata(typeOrFunc: any, parentCtor: any): {[key: string]: any[]} {
|
2015-09-03 18:10:48 -04:00
|
|
|
// Prefer the direct API.
|
2016-11-18 18:17:44 -05:00
|
|
|
if ((<any>typeOrFunc).propMetadata &&
|
|
|
|
(<any>typeOrFunc).propMetadata !== parentCtor.propMetadata) {
|
2016-10-06 18:11:16 -04:00
|
|
|
let propMetadata = (<any>typeOrFunc).propMetadata;
|
2016-10-19 16:42:39 -04:00
|
|
|
if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
|
2015-09-03 18:10:48 -04:00
|
|
|
propMetadata = propMetadata.propMetadata;
|
|
|
|
}
|
|
|
|
return propMetadata;
|
|
|
|
}
|
2016-05-02 01:47:25 -04:00
|
|
|
|
|
|
|
// API of tsickle for lowering decorators to properties on the class.
|
2016-11-18 18:17:44 -05:00
|
|
|
if ((<any>typeOrFunc).propDecorators &&
|
|
|
|
(<any>typeOrFunc).propDecorators !== parentCtor.propDecorators) {
|
2016-10-06 18:11:16 -04:00
|
|
|
const propDecorators = (<any>typeOrFunc).propDecorators;
|
|
|
|
const propMetadata = <{[key: string]: any[]}>{};
|
2016-06-08 19:38:52 -04:00
|
|
|
Object.keys(propDecorators).forEach(prop => {
|
|
|
|
propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
|
|
|
|
});
|
2016-05-02 01:47:25 -04:00
|
|
|
return propMetadata;
|
|
|
|
}
|
|
|
|
|
|
|
|
// API for metadata created by invoking the decorators.
|
2016-11-18 18:17:44 -05:00
|
|
|
if (this._reflect && this._reflect.getOwnMetadata) {
|
|
|
|
return this._reflect.getOwnMetadata('propMetadata', typeOrFunc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
2016-12-27 19:58:52 -05:00
|
|
|
if (!isType(typeOrFunc)) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const parentCtor = getParentCtor(typeOrFunc);
|
2016-11-18 18:17:44 -05:00
|
|
|
const propMetadata: {[key: string]: any[]} = {};
|
|
|
|
if (parentCtor !== Object) {
|
|
|
|
const parentPropMetadata = this.propMetadata(parentCtor);
|
|
|
|
Object.keys(parentPropMetadata).forEach((propName) => {
|
|
|
|
propMetadata[propName] = parentPropMetadata[propName];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor);
|
|
|
|
if (ownPropMetadata) {
|
|
|
|
Object.keys(ownPropMetadata).forEach((propName) => {
|
|
|
|
const decorators: any[] = [];
|
|
|
|
if (propMetadata.hasOwnProperty(propName)) {
|
|
|
|
decorators.push(...propMetadata[propName]);
|
|
|
|
}
|
|
|
|
decorators.push(...ownPropMetadata[propName]);
|
|
|
|
propMetadata[propName] = decorators;
|
|
|
|
});
|
2015-09-03 18:10:48 -04:00
|
|
|
}
|
2016-11-18 18:17:44 -05:00
|
|
|
return propMetadata;
|
2015-09-03 18:10:48 -04:00
|
|
|
}
|
|
|
|
|
2016-10-12 13:05:32 -04:00
|
|
|
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
2016-10-11 21:42:00 -04:00
|
|
|
return type instanceof Type && lcProperty in type.prototype;
|
2015-07-07 23:03:00 -04:00
|
|
|
}
|
2015-05-27 11:07:11 -04:00
|
|
|
|
2015-05-18 19:09:09 -04:00
|
|
|
getter(name: string): GetterFn { return <GetterFn>new Function('o', 'return o.' + name + ';'); }
|
2015-04-24 16:17:36 -04:00
|
|
|
|
2015-05-18 19:09:09 -04:00
|
|
|
setter(name: string): SetterFn {
|
|
|
|
return <SetterFn>new Function('o', 'v', 'return o.' + name + ' = v;');
|
|
|
|
}
|
2015-04-24 16:17:36 -04:00
|
|
|
|
|
|
|
method(name: string): MethodFn {
|
2016-10-06 18:11:16 -04:00
|
|
|
const functionBody = `if (!o.${name}) throw new Error('"${name}" is undefined');
|
2015-04-24 16:17:36 -04:00
|
|
|
return o.${name}.apply(o, args);`;
|
2015-05-18 19:09:09 -04:00
|
|
|
return <MethodFn>new Function('o', 'args', functionBody);
|
2015-04-24 16:17:36 -04:00
|
|
|
}
|
2015-08-19 19:56:46 -04:00
|
|
|
|
|
|
|
// There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
2016-05-03 20:31:40 -04:00
|
|
|
importUri(type: any): string {
|
|
|
|
// StaticSymbol
|
|
|
|
if (typeof type === 'object' && type['filePath']) {
|
|
|
|
return type['filePath'];
|
|
|
|
}
|
|
|
|
// Runtime type
|
|
|
|
return `./${stringify(type)}`;
|
|
|
|
}
|
2016-08-24 20:39:49 -04:00
|
|
|
|
2017-03-20 19:31:11 -04:00
|
|
|
resourceUri(type: any): string { return `./${stringify(type)}`; }
|
|
|
|
|
2017-02-14 16:33:06 -05:00
|
|
|
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any {
|
|
|
|
return runtime;
|
|
|
|
}
|
2016-08-29 11:52:25 -04:00
|
|
|
resolveEnum(enumIdentifier: any, name: string): any { return enumIdentifier[name]; }
|
2015-04-24 16:17:36 -04:00
|
|
|
}
|
2016-05-02 01:47:25 -04:00
|
|
|
|
|
|
|
function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] {
|
|
|
|
if (!decoratorInvocations) {
|
|
|
|
return [];
|
|
|
|
}
|
2016-05-02 13:36:58 -04:00
|
|
|
return decoratorInvocations.map(decoratorInvocation => {
|
2016-09-12 23:30:42 -04:00
|
|
|
const decoratorType = decoratorInvocation.type;
|
|
|
|
const annotationCls = decoratorType.annotationCls;
|
|
|
|
const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
|
|
|
|
return new annotationCls(...annotationArgs);
|
2016-05-02 01:47:25 -04:00
|
|
|
});
|
|
|
|
}
|
2016-12-27 19:58:52 -05:00
|
|
|
|
|
|
|
function getParentCtor(ctor: Function): Type<any> {
|
|
|
|
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;
|
|
|
|
}
|