From 3d52174bf1cad9531b4d4985d3b5f7e326b3eb1f Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 26 Jun 2018 10:44:05 -0700 Subject: [PATCH] feat(ivy): JIT support for compilation of @Pipes (#24703) Adds support for compiling @Pipe in JIT mode, along with tests to verify that certain aspects of compilation are correct. PR Close #24703 --- packages/core/src/ivy_switch_legacy.ts | 1 + packages/core/src/ivy_switch_on.ts | 2 + packages/core/src/metadata/directives.ts | 6 ++- packages/core/src/render3/jit/pipe.ts | 40 +++++++++++++++++++ packages/core/test/render3/ivy/jit_spec.ts | 25 +++++++++++- .../core/test/render3/jit_environment_spec.ts | 1 + 6 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 packages/core/src/render3/jit/pipe.ts diff --git a/packages/core/src/ivy_switch_legacy.ts b/packages/core/src/ivy_switch_legacy.ts index 0da653c104..3b50412715 100644 --- a/packages/core/src/ivy_switch_legacy.ts +++ b/packages/core/src/ivy_switch_legacy.ts @@ -11,3 +11,4 @@ export const R3_COMPILE_COMPONENT: ((type: any, meta: any) => void)|null = null; export const R3_COMPILE_DIRECTIVE: ((type: any, meta: any) => void)|null = null; export const R3_COMPILE_INJECTABLE: ((type: any, meta: any) => void)|null = null; export const R3_COMPILE_NGMODULE: ((type: any, meta: any) => void)|null = null; +export const R3_COMPILE_PIPE: ((type: any, meta: any) => void)|null = null; diff --git a/packages/core/src/ivy_switch_on.ts b/packages/core/src/ivy_switch_on.ts index 8d29031c72..9c21b13a7b 100644 --- a/packages/core/src/ivy_switch_on.ts +++ b/packages/core/src/ivy_switch_on.ts @@ -9,9 +9,11 @@ import {compileComponent, compileDirective} from './render3/jit/directive'; import {compileInjectable} from './render3/jit/injectable'; import {compileNgModule} from './render3/jit/module'; +import {compilePipe} from './render3/jit/pipe'; export const ivyEnabled = true; export const R3_COMPILE_COMPONENT = compileComponent; export const R3_COMPILE_DIRECTIVE = compileDirective; export const R3_COMPILE_INJECTABLE = compileInjectable; export const R3_COMPILE_NGMODULE = compileNgModule; +export const R3_COMPILE_PIPE = compilePipe; diff --git a/packages/core/src/metadata/directives.ts b/packages/core/src/metadata/directives.ts index 4a5692cbb4..4c5a08f544 100644 --- a/packages/core/src/metadata/directives.ts +++ b/packages/core/src/metadata/directives.ts @@ -8,7 +8,7 @@ import {ChangeDetectionStrategy} from '../change_detection/constants'; import {Provider} from '../di'; -import {R3_COMPILE_COMPONENT, R3_COMPILE_DIRECTIVE} from '../ivy_switch'; +import {R3_COMPILE_COMPONENT, R3_COMPILE_DIRECTIVE, R3_COMPILE_PIPE} from '../ivy_switch'; import {Type} from '../type'; import {TypeDecorator, makeDecorator, makePropDecorator} from '../util/decorators'; import {ViewEncapsulation} from './view'; @@ -810,7 +810,9 @@ export interface Pipe { * * @Annotation */ -export const Pipe: PipeDecorator = makeDecorator('Pipe', (p: Pipe) => ({pure: true, ...p})); +export const Pipe: PipeDecorator = makeDecorator( + 'Pipe', (p: Pipe) => ({pure: true, ...p}), undefined, undefined, + (type: Type, meta: Pipe) => (R3_COMPILE_PIPE || (() => {}))(type, meta)); /** diff --git a/packages/core/src/render3/jit/pipe.ts b/packages/core/src/render3/jit/pipe.ts new file mode 100644 index 0000000000..57abcb9d55 --- /dev/null +++ b/packages/core/src/render3/jit/pipe.ts @@ -0,0 +1,40 @@ +/** + * @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 + */ + +import {Expression, WrappedNodeExpr, compilePipeFromMetadata, jitExpression} from '@angular/compiler'; + +import {Pipe} from '../../metadata/directives'; +import {Type} from '../../type'; +import {stringify} from '../util'; + +import {angularCoreEnv} from './environment'; +import {NG_PIPE_DEF} from './fields'; +import {reflectDependencies} from './util'; + +export function compilePipe(type: Type, meta: Pipe): void { + let def: any = null; + Object.defineProperty(type, NG_PIPE_DEF, { + get: () => { + if (def === null) { + const sourceMapUrl = `ng://${stringify(type)}/ngPipeDef.js`; + + const name = type.name; + const res = compilePipeFromMetadata({ + name, + type: new WrappedNodeExpr(type), + deps: reflectDependencies(type), + pipeName: meta.name, + pure: meta.pure !== undefined ? meta.pure : true, + }); + + def = jitExpression(res.expression, angularCoreEnv, sourceMapUrl); + } + return def; + } + }); +} diff --git a/packages/core/test/render3/ivy/jit_spec.ts b/packages/core/test/render3/ivy/jit_spec.ts index 919df879d8..3e16eea884 100644 --- a/packages/core/test/render3/ivy/jit_spec.ts +++ b/packages/core/test/render3/ivy/jit_spec.ts @@ -12,9 +12,9 @@ import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs'; import {Injectable} from '@angular/core/src/di/injectable'; import {inject, setCurrentInjector} from '@angular/core/src/di/injector'; import {ivyEnabled} from '@angular/core/src/ivy_switch'; -import {Component, HostBinding, HostListener} from '@angular/core/src/metadata/directives'; +import {Component, HostBinding, HostListener, Pipe} from '@angular/core/src/metadata/directives'; import {NgModule, NgModuleDefInternal} from '@angular/core/src/metadata/ng_module'; -import {ComponentDefInternal} from '@angular/core/src/render3/interfaces/definition'; +import {ComponentDefInternal, PipeDefInternal} from '@angular/core/src/render3/interfaces/definition'; ivyEnabled && describe('render3 jit', () => { let injector: any; @@ -212,6 +212,27 @@ ivyEnabled && describe('render3 jit', () => { expect(cmpDef.hostBindings).toBeDefined(); expect(cmpDef.hostBindings !.length).toBe(2); }); + + it('should compile @Pipes without errors', () => { + @Pipe({name: 'test-pipe', pure: false}) + class P { + } + + const pipeDef = (P as any).ngPipeDef as PipeDefInternal

; + expect(pipeDef.name).toBe('test-pipe'); + expect(pipeDef.pure).toBe(false, 'pipe should not be pure'); + expect(pipeDef.factory() instanceof P) + .toBe(true, 'factory() should create an instance of the pipe'); + }); + + it('should default @Pipe to pure: true', () => { + @Pipe({name: 'test-pipe'}) + class P { + } + + const pipeDef = (P as any).ngPipeDef as PipeDefInternal

; + expect(pipeDef.pure).toBe(true, 'pipe should be pure'); + }); }); it('ensure at least one spec exists', () => {}); diff --git a/packages/core/test/render3/jit_environment_spec.ts b/packages/core/test/render3/jit_environment_spec.ts index 0927739564..ea7e22b898 100644 --- a/packages/core/test/render3/jit_environment_spec.ts +++ b/packages/core/test/render3/jit_environment_spec.ts @@ -16,6 +16,7 @@ const INTERFACE_EXCEPTIONS = new Set([ 'DirectiveDef', 'InjectorDef', 'NgModuleDef', + 'ɵPipeDef', ]); describe('r3 jit environment', () => {