From 0dbf95954851d400624c0d2b71aebcb50854321d Mon Sep 17 00:00:00 2001 From: Chuck Jazdzewski Date: Thu, 24 Mar 2016 10:03:10 -0700 Subject: [PATCH] feat(static-reflector): Added StaticReflector Added a static reflector that uses metadta produced during build or, additionally, directly from typescript, to produce the metadata used by Angular code generation compiler. --- .../angular2/src/compiler/static_reflector.ts | 499 ++++++++++++++++++ .../test/compiler/static_reflector_spec.ts | 441 ++++++++++++++++ 2 files changed, 940 insertions(+) create mode 100644 modules/angular2/src/compiler/static_reflector.ts create mode 100644 modules/angular2/test/compiler/static_reflector_spec.ts diff --git a/modules/angular2/src/compiler/static_reflector.ts b/modules/angular2/src/compiler/static_reflector.ts new file mode 100644 index 0000000000..4a15eeccd4 --- /dev/null +++ b/modules/angular2/src/compiler/static_reflector.ts @@ -0,0 +1,499 @@ +import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; +import { + isArray, + isBlank, + isNumber, + isPresent, + isPrimitive, + isString, + Type +} from 'angular2/src/facade/lang'; +import { + AttributeMetadata, + DirectiveMetadata, + ComponentMetadata, + ContentChildrenMetadata, + ContentChildMetadata, + InputMetadata, + HostBindingMetadata, + HostListenerMetadata, + OutputMetadata, + PipeMetadata, + ViewMetadata, + ViewChildMetadata, + ViewChildrenMetadata, + ViewQueryMetadata, + QueryMetadata, +} from 'angular2/src/core/metadata'; + +/** + * The host of the static resolver is expected to be able to provide module metadata in the form of + * ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is + * produced and the module has exported variables or classes with decorators. Module metadata can + * also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata. + */ +export interface StaticReflectorHost { + /** + * Return a ModuleMetadata for the give module. + * + * @param moduleId is a string identifier for a module in the form that would expected in a + * module import of an import statement. + * @returns the metadata for the given module. + */ + getMetadataFor(moduleId: string): {[key: string]: any}; +} + +/** + * A token representing the a reference to a static type. + * + * This token is unique for a moduleId and name and can be used as a hash table key. + */ +export class StaticType { + constructor(public moduleId: string, public name: string) {} +} + +/** + * A static reflector implements enough of the Reflector API that is necessary to compile + * templates statically. + */ +export class StaticReflector { + private typeCache = new Map(); + private annotationCache = new Map(); + private propertyCache = new Map(); + private parameterCache = new Map(); + private metadataCache = new Map(); + + constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); } + + /** + * getStatictype produces a Type whose metadata is known but whose implementation is not loaded. + * All types passed to the StaticResolver should be pseudo-types returned by this method. + * + * @param moduleId the module identifier as would be passed to an import statement. + * @param name the name of the type. + */ + public getStaticType(moduleId: string, name: string): StaticType { + let key = `"${moduleId}".${name}`; + let result = this.typeCache.get(key); + if (!isPresent(result)) { + result = new StaticType(moduleId, name); + this.typeCache.set(key, result); + } + return result; + } + + public annotations(type: StaticType): any[] { + let annotations = this.annotationCache.get(type); + if (!isPresent(annotations)) { + let classMetadata = this.getTypeMetadata(type); + if (isPresent(classMetadata['decorators'])) { + annotations = (classMetadata['decorators']) + .map(decorator => this.convertKnownDecorator(type.moduleId, decorator)) + .filter(decorator => isPresent(decorator)); + } + this.annotationCache.set(type, annotations); + } + return annotations; + } + + public propMetadata(type: StaticType): {[key: string]: any} { + let propMetadata = this.propertyCache.get(type); + if (!isPresent(propMetadata)) { + let classMetadata = this.getTypeMetadata(type); + propMetadata = this.getPropertyMetadata(type.moduleId, classMetadata['members']); + this.propertyCache.set(type, propMetadata); + } + return propMetadata; + } + + public parameters(type: StaticType): any[] { + let parameters = this.parameterCache.get(type); + if (!isPresent(parameters)) { + let classMetadata = this.getTypeMetadata(type); + let ctorData = classMetadata['members']['__ctor__']; + if (isPresent(ctorData)) { + let ctor = (ctorData).find(a => a['__symbolic'] === 'constructor'); + parameters = this.simplify(type.moduleId, ctor['parameters']); + this.parameterCache.set(type, parameters); + } + } + return parameters; + } + + private conversionMap = new Map any>(); + private initializeConversionMap() { + let core_metadata = 'angular2/src/core/metadata'; + let conversionMap = this.conversionMap; + conversionMap.set(this.getStaticType(core_metadata, 'Directive'), + (moduleContext, expression) => { + let p0 = this.getDecoratorParameter(moduleContext, expression, 0); + if (!isPresent(p0)) { + p0 = {}; + } + return new DirectiveMetadata({ + selector: p0['selector'], + inputs: p0['inputs'], + outputs: p0['outputs'], + events: p0['events'], + host: p0['host'], + bindings: p0['bindings'], + providers: p0['providers'], + exportAs: p0['exportAs'], + queries: p0['queries'], + }); + }); + conversionMap.set(this.getStaticType(core_metadata, 'Component'), + (moduleContext, expression) => { + let p0 = this.getDecoratorParameter(moduleContext, expression, 0); + if (!isPresent(p0)) { + p0 = {}; + } + return new ComponentMetadata({ + selector: p0['selector'], + inputs: p0['inputs'], + outputs: p0['outputs'], + properties: p0['properties'], + events: p0['events'], + host: p0['host'], + exportAs: p0['exportAs'], + moduleId: p0['moduleId'], + bindings: p0['bindings'], + providers: p0['providers'], + viewBindings: p0['viewBindings'], + viewProviders: p0['viewProviders'], + changeDetection: p0['changeDetection'], + queries: p0['queries'], + templateUrl: p0['templateUrl'], + template: p0['template'], + styleUrls: p0['styleUrls'], + styles: p0['styles'], + directives: p0['directives'], + pipes: p0['pipes'], + encapsulation: p0['encapsulation'] + }); + }); + conversionMap.set(this.getStaticType(core_metadata, 'Input'), + (moduleContext, expression) => new InputMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'Output'), + (moduleContext, expression) => new OutputMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'View'), (moduleContext, expression) => { + let p0 = this.getDecoratorParameter(moduleContext, expression, 0); + if (!isPresent(p0)) { + p0 = {}; + } + return new ViewMetadata({ + templateUrl: p0['templateUrl'], + template: p0['template'], + directives: p0['directives'], + pipes: p0['pipes'], + encapsulation: p0['encapsulation'], + styles: p0['styles'], + }); + }); + conversionMap.set(this.getStaticType(core_metadata, 'Attribute'), + (moduleContext, expression) => new AttributeMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'Query'), (moduleContext, expression) => { + let p0 = this.getDecoratorParameter(moduleContext, expression, 0); + let p1 = this.getDecoratorParameter(moduleContext, expression, 1); + if (!isPresent(p1)) { + p1 = {}; + } + return new QueryMetadata(p0, {descendants: p1.descendants, first: p1.first}); + }); + conversionMap.set(this.getStaticType(core_metadata, 'ContentChildren'), + (moduleContext, expression) => new ContentChildrenMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'ContentChild'), + (moduleContext, expression) => new ContentChildMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'ViewChildren'), + (moduleContext, expression) => new ViewChildrenMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'ViewChild'), + (moduleContext, expression) => new ViewChildMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'ViewQuery'), + (moduleContext, expression) => { + let p0 = this.getDecoratorParameter(moduleContext, expression, 0); + let p1 = this.getDecoratorParameter(moduleContext, expression, 1); + if (!isPresent(p1)) { + p1 = {}; + } + return new ViewQueryMetadata(p0, { + descendants: p1['descendants'], + first: p1['first'], + }); + }); + conversionMap.set(this.getStaticType(core_metadata, 'Pipe'), (moduleContext, expression) => { + let p0 = this.getDecoratorParameter(moduleContext, expression, 0); + if (!isPresent(p0)) { + p0 = {}; + } + return new PipeMetadata({ + name: p0['name'], + pure: p0['pure'], + }); + }); + conversionMap.set(this.getStaticType(core_metadata, 'HostBinding'), + (moduleContext, expression) => new HostBindingMetadata( + this.getDecoratorParameter(moduleContext, expression, 0))); + conversionMap.set(this.getStaticType(core_metadata, 'HostListener'), + (moduleContext, expression) => new HostListenerMetadata( + this.getDecoratorParameter(moduleContext, expression, 0), + this.getDecoratorParameter(moduleContext, expression, 1))); + } + + private convertKnownDecorator(moduleContext: string, expression: {[key: string]: any}): any { + let converter = this.conversionMap.get(this.getDecoratorType(moduleContext, expression)); + if (isPresent(converter)) return converter(moduleContext, expression); + return null; + } + + private getDecoratorType(moduleContext: string, expression: {[key: string]: any}): StaticType { + if (isMetadataSymbolicCallExpression(expression)) { + let target = expression['expression']; + if (isMetadataSymbolicReferenceExpression(target)) { + let moduleId = this.normalizeModuleName(moduleContext, target['module']); + return this.getStaticType(moduleId, target['name']); + } + } + return null; + } + + private getDecoratorParameter(moduleContext: string, expression: {[key: string]: any}, + index: number): any { + if (isMetadataSymbolicCallExpression(expression) && isPresent(expression['arguments']) && + (expression['arguments']).length <= index + 1) { + return this.simplify(moduleContext, (expression['arguments'])[index]); + } + return null; + } + + private getPropertyMetadata(moduleContext: string, + value: {[key: string]: any}): {[key: string]: any} { + if (isPresent(value)) { + let result = {}; + StringMapWrapper.forEach(value, (value, name) => { + let data = this.getMemberData(moduleContext, value); + if (isPresent(data)) { + let propertyData = data.filter(d => d['kind'] == "property") + .map(d => d['directives']) + .reduce((p, c) => (p).concat(c), []); + if (propertyData.length != 0) { + StringMapWrapper.set(result, name, propertyData); + } + } + }); + return result; + } + return null; + } + + // clang-format off + private getMemberData(moduleContext: string, member: { [key: string]: any }[]): { [key: string]: any }[] { + // clang-format on + let result = []; + if (isPresent(member)) { + for (let item of member) { + result.push({ + kind: item['__symbolic'], + directives: + isPresent(item['decorators']) ? + (item['decorators']) + .map(decorator => this.convertKnownDecorator(moduleContext, decorator)) + .filter(d => isPresent(d)) : + null + }); + } + } + return result; + } + + /** @internal */ + public simplify(moduleContext: string, value: any): any { + let _this = this; + + function simplify(expression: any): any { + if (isPrimitive(expression)) { + return expression; + } + if (isArray(expression)) { + let result = []; + for (let item of(expression)) { + result.push(simplify(item)); + } + return result; + } + if (isPresent(expression)) { + if (isPresent(expression['__symbolic'])) { + switch (expression['__symbolic']) { + case "binop": + let left = simplify(expression['left']); + let right = simplify(expression['right']); + switch (expression['operator']) { + case '&&': + return left && right; + case '||': + return left || right; + case '|': + return left | right; + case '^': + return left ^ right; + case '&': + return left & right; + case '==': + return left == right; + case '!=': + return left != right; + case '===': + return left === right; + case '!==': + return left !== right; + case '<': + return left < right; + case '>': + return left > right; + case '<=': + return left <= right; + case '>=': + return left >= right; + case '<<': + return left << right; + case '>>': + return left >> right; + case '+': + return left + right; + case '-': + return left - right; + case '*': + return left * right; + case '/': + return left / right; + case '%': + return left % right; + } + return null; + case "pre": + let operand = simplify(expression['operand']); + switch (expression['operator']) { + case '+': + return operand; + case '-': + return -operand; + case '!': + return !operand; + case '~': + return ~operand; + } + return null; + case "index": + let indexTarget = simplify(expression['expression']); + let index = simplify(expression['index']); + if (isPresent(indexTarget) && isPrimitive(index)) return indexTarget[index]; + return null; + case "select": + let selectTarget = simplify(expression['expression']); + let member = simplify(expression['member']); + if (isPresent(selectTarget) && isPrimitive(member)) return selectTarget[member]; + return null; + case "reference": + let referenceModuleName = + _this.normalizeModuleName(moduleContext, expression['module']); + let referenceModule = _this.getModuleMetadata(referenceModuleName); + let referenceValue = referenceModule['metadata'][expression['name']]; + if (isClassMetadata(referenceValue)) { + // Convert to a pseudo type + return _this.getStaticType(referenceModuleName, expression['name']); + } + return _this.simplify(referenceModuleName, referenceValue); + case "call": + return null; + } + return null; + } + let result = {}; + StringMapWrapper.forEach(expression, (value, name) => { result[name] = simplify(value); }); + return result; + } + return null; + } + + return simplify(value); + } + + private getModuleMetadata(module: string): {[key: string]: any} { + let moduleMetadata = this.metadataCache.get(module); + if (!isPresent(moduleMetadata)) { + moduleMetadata = this.host.getMetadataFor(module); + if (!isPresent(moduleMetadata)) { + moduleMetadata = {__symbolic: "module", module: module, metadata: {}}; + } + this.metadataCache.set(module, moduleMetadata); + } + return moduleMetadata; + } + + private getTypeMetadata(type: StaticType): {[key: string]: any} { + let moduleMetadata = this.getModuleMetadata(type.moduleId); + let result = moduleMetadata['metadata'][type.name]; + if (!isPresent(result)) { + result = {__symbolic: "class"}; + } + return result; + } + + private normalizeModuleName(from: string, to: string): string { + if (to.startsWith('.')) { + return pathTo(from, to); + } + return to; + } +} + +function isMetadataSymbolicCallExpression(expression: any): boolean { + return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'call'; +} + +function isMetadataSymbolicReferenceExpression(expression: any): boolean { + return !isPrimitive(expression) && !isArray(expression) && + expression['__symbolic'] == 'reference'; +} + +function isClassMetadata(expression: any): boolean { + return !isPrimitive(expression) && !isArray(expression) && expression['__symbolic'] == 'class'; +} + +function splitPath(path: string): string[] { + return path.split(/\/|\\/g); +} + +function resolvePath(pathParts: string[]): string { + let result = []; + ListWrapper.forEachWithIndex(pathParts, (part, index) => { + switch (part) { + case '': + case '.': + if (index > 0) return; + break; + case '..': + if (index > 0 && result.length != 0) result.pop(); + return; + } + result.push(part); + }); + return result.join('/'); +} + +function pathTo(from: string, to: string): string { + let result = to; + if (to.startsWith('.')) { + let fromParts = splitPath(from); + fromParts.pop(); // remove the file name. + let toParts = splitPath(to); + result = resolvePath(fromParts.concat(toParts)); + } + return result; +} diff --git a/modules/angular2/test/compiler/static_reflector_spec.ts b/modules/angular2/test/compiler/static_reflector_spec.ts new file mode 100644 index 0000000000..fcb361a551 --- /dev/null +++ b/modules/angular2/test/compiler/static_reflector_spec.ts @@ -0,0 +1,441 @@ +import { + ddescribe, + describe, + xdescribe, + it, + iit, + xit, + expect, + beforeEach, + afterEach, + AsyncTestCompleter, + inject, + beforeEachProviders +} from 'angular2/testing_internal'; + +import {StaticReflector, StaticReflectorHost} from 'angular2/src/compiler/static_reflector'; + +export function main() { + describe('StaticRefelector', () => { + it('should get annotations for NgFor', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + let NgFor = reflector.getStaticType('angular2/src/common/directives/ng_for', 'NgFor'); + let annotations = reflector.annotations(NgFor); + expect(annotations.length).toEqual(1); + let annotation = annotations[0]; + expect(annotation.selector).toEqual('[ngFor][ngForOf]'); + expect(annotation.inputs).toEqual(['ngForTrackBy', 'ngForOf', 'ngForTemplate']); + }); + + it('should get constructor for NgFor', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + let NgFor = reflector.getStaticType('angular2/src/common/directives/ng_for', 'NgFor'); + let ViewContainerRef = reflector.getStaticType('angular2/src/core/linker/view_container_ref', + 'ViewContainerRef'); + let TemplateRef = + reflector.getStaticType('angular2/src/core/linker/template_ref', 'TemplateRef'); + let IterableDiffers = reflector.getStaticType( + 'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers'); + let ChangeDetectorRef = reflector.getStaticType( + 'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef'); + + let parameters = reflector.parameters(NgFor); + expect(parameters) + .toEqual([ViewContainerRef, TemplateRef, IterableDiffers, ChangeDetectorRef]); + }); + + it('should get annotations for HeroDetailComponent', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + let HeroDetailComponent = + reflector.getStaticType('./app/hero-detail.component', 'HeroDetailComponent'); + let annotations = reflector.annotations(HeroDetailComponent); + expect(annotations.length).toEqual(1); + let annotation = annotations[0]; + expect(annotation.selector).toEqual('my-hero-detail'); + }); + + it('should get propMetadata for HeroDetailComponent', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + let HeroDetailComponent = + reflector.getStaticType('./app/hero-detail.component', 'HeroDetailComponent'); + let props = reflector.propMetadata(HeroDetailComponent); + expect(props['hero']).toBeTruthy(); + }); + + it('should simplify primitive into itself', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', 1)).toBe(1); + expect(reflector.simplify('', true)).toBe(true); + expect(reflector.simplify('', "some value")).toBe("some value"); + }); + + it('should simplify an array into a copy of the array', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', [1, 2, 3])).toEqual([1, 2, 3]); + }); + + it('should simplify an object to a copy of the object', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + let expr = {a: 1, b: 2, c: 3}; + expect(reflector.simplify('', expr)).toEqual(expr); + }); + + it('should simplify &&', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: true, right: true}))).toBe(true); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: true, right: false}))).toBe(false); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: false, right: true}))).toBe(false); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&&', left: false, right: false}))).toBe(false); + }); + + it('should simplify ||', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: true, right: true}))).toBe(true); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: true, right: false}))).toBe(true); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: false, right: true}))).toBe(true); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '||', left: false, right: false}))).toBe(false); + }); + + it('should simplify &', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0x0F}))).toBe(0x22 & 0x0F); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '&', left: 0x22, right: 0xF0}))).toBe(0x22 & 0xF0); + }); + + it('should simplify |', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0); + }); + + it('should simplify ^', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F}))).toBe(0x22 | 0x0F); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0}))).toBe(0x22 | 0xF0); + }); + + it('should simplify ==', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0x22}))).toBe(0x22 == 0x22); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '==', left: 0x22, right: 0xF0}))).toBe(0x22 == 0xF0); + }); + + it('should simplify !=', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0x22}))).toBe(0x22 != 0x22); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!=', left: 0x22, right: 0xF0}))).toBe(0x22 != 0xF0); + }); + + it('should simplify ===', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0x22}))).toBe(0x22 === 0x22); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '===', left: 0x22, right: 0xF0}))).toBe(0x22 === 0xF0); + }); + + it('should simplify !==', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0x22}))).toBe(0x22 !== 0x22); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '!==', left: 0x22, right: 0xF0}))).toBe(0x22 !== 0xF0); + }); + + it('should simplify >', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 1, right: 1}))).toBe(1 > 1); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 1, right: 0}))).toBe(1 > 0); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>', left: 0, right: 1}))).toBe(0 > 1); + }); + + it('should simplify >=', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 1, right: 1}))).toBe(1 >= 1); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 1, right: 0}))).toBe(1 >= 0); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>=', left: 0, right: 1}))).toBe(0 >= 1); + }); + + it('should simplify <=', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 1, right: 1}))).toBe(1 <= 1); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 1, right: 0}))).toBe(1 <= 0); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<=', left: 0, right: 1}))).toBe(0 <= 1); + }); + + it('should simplify <', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 1, right: 1}))).toBe(1 < 1); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 1, right: 0}))).toBe(1 < 0); + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<', left: 0, right: 1}))).toBe(0 < 1); + }); + + it('should simplify <<', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '<<', left: 0x55, right: 2}))).toBe(0x55 << 2); + }); + + it('should simplify >>', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '>>', left: 0x55, right: 2}))).toBe(0x55 >> 2); + }); + + it('should simplify +', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '+', left: 0x55, right: 2}))).toBe(0x55 + 2); + }); + + it('should simplify -', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '-', left: 0x55, right: 2}))).toBe(0x55 - 2); + }); + + it('should simplify *', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '*', left: 0x55, right: 2}))).toBe(0x55 * 2); + }); + + it('should simplify /', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '/', left: 0x55, right: 2}))).toBe(0x55 / 2); + }); + + it('should simplify %', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'binop', operator: '%', left: 0x55, right: 2}))).toBe(0x55 % 2); + }); + + it('should simplify prefix -', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '-', operand: 2}))).toBe(-2); + }); + + it('should simplify prefix ~', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '~', operand: 2}))).toBe(~2); + }); + + it('should simplify prefix !', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '!', operand: true}))).toBe(!true); + expect(reflector.simplify('', ({ __symbolic: 'pre', operator: '!', operand: false}))).toBe(!false); + }); + + it('should simpify an array index', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect(reflector.simplify('', ({__symbolic: "index", expression: [1, 2, 3], index: 2}))) + .toBe(3); + }); + + it('should simplify an object index', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + let expr = {__symbolic: "select", expression: {a: 1, b: 2, c: 3}, member: "b"}; + expect(reflector.simplify('', expr)).toBe(2); + }); + + it('should simplify a module reference', () => { + let host = new MockReflectorHost(); + let reflector = new StaticReflector(host); + + expect( + reflector.simplify('./cases', ({__symbolic: "reference", module: "./extern", name: "s"}))) + .toEqual("s"); + }); + }); +} + +class MockReflectorHost implements StaticReflectorHost { + getMetadataFor(moduleId: string): any { + return { + 'angular2/src/common/directives/ng_for': + { + "__symbolic": "module", + "module": "./ng_for", + "metadata": + { + "NgFor": + { + "__symbolic": "class", + "decorators": + [ + { + "__symbolic": "call", + "expression": { + "__symbolic": "reference", + "name": "Directive", + "module": "../../core/metadata" + }, + "arguments": + [ + { + "selector": "[ngFor][ngForOf]", + "inputs": ["ngForTrackBy", "ngForOf", "ngForTemplate"] + } + ] + } + ], + "members": + { + "__ctor__": [ + { + "__symbolic": "constructor", + "parameters": + [ + { + "__symbolic": "reference", + "module": "../../core/linker/view_container_ref", + "name": "ViewContainerRef" + }, + { + "__symbolic": "reference", + "module": "../../core/linker/template_ref", + "name": "TemplateRef" + }, + { + "__symbolic": "reference", + "module": + "../../core/change_detection/differs/iterable_differs", + "name": "IterableDiffers" + }, + { + "__symbolic": "reference", + "module": + "../../core/change_detection/change_detector_ref", + "name": "ChangeDetectorRef" + } + ] + } + ] + } + } + } + }, + 'angular2/src/core/linker/view_container_ref': + { + "module": "./view_container_ref", + "metadata": {"ViewContainerRef": {"__symbolic": "class"}} + }, + 'angular2/src/core/linker/template_ref': + {"module": "./template_ref", "metadata": {"TemplateRef": {"__symbolic": "class"}}}, + 'angular2/src/core/change_detection/differs/iterable_differs': + { + "module": "./iterable_differs", + "metadata": {"IterableDiffers": {"__symbolic": "class"}} + }, + 'angular2/src/core/change_detection/change_detector_ref': + { + "module": "./change_detector_ref", + "metadata": {"ChangeDetectorRef": {"__symbolic": "class"}} + }, + './app/hero-detail.component': + { + "__symbolic": "module", + "module": "./hero-detail.component", + "metadata": + { + "HeroDetailComponent": + { + "__symbolic": "class", + "decorators": + [ + { + "__symbolic": "call", + "expression": { + "__symbolic": "reference", + "name": "Component", + "module": "angular2/src/core/metadata" + }, + "arguments": [ + { + "selector": "my-hero-detail", + "template": "\n
\n

{{hero.name}} details!

\n
{{hero.id}}
\n
\n \n \n
\n
\n" + } + ] + } + ], + "members": + { + "hero": [ + { + "__symbolic": "property", + "decorators": + [ + { + "__symbolic": "call", + "expression": + { + "__symbolic": "reference", + "name": "Input", + "module": "angular2/src/core/metadata" + } + } + ] + } + ] + } + } + } + }, + './extern': { + "__symbolic": "module", module: './extern', metadata: { s: "s" } + } + } + [moduleId]; + } +}