Skip to content

Commit

Permalink
feat(ivy): add an IVY local the compiler which avoids analyzeModule
Browse files Browse the repository at this point in the history
closes #23289

Based on a spike by @chukjaz
  • Loading branch information
vicb committed Apr 19, 2018
1 parent 0deb21e commit 20bd15a
Show file tree
Hide file tree
Showing 17 changed files with 1,056 additions and 462 deletions.
63 changes: 45 additions & 18 deletions packages/compiler/src/aot/compiler.ts
Expand Up @@ -6,23 +6,27 @@
* found in the LICENSE file at https://angular.io/license
*/

import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileInjectableMetadata, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileShallowModuleMetadata, CompileStylesheetMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, flatten, identifierName, templateSourceUrl, tokenReference} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileInjectableMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileShallowModuleMetadata, CompileStylesheetMetadata, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, flatten, identifierName, templateSourceUrl} from '../compile_metadata';
import {CompilerConfig} from '../config';
import {ConstantPool} from '../constant_pool';
import {ViewEncapsulation} from '../core';
import {MessageBundle} from '../i18n/message_bundle';
import {Identifiers, createTokenForExternalReference} from '../identifiers';
import {InjectableCompiler} from '../injectable_compiler';
import {CompileMetadataResolver} from '../metadata_resolver';
import * as html from '../ml_parser/ast';
import {HtmlParser} from '../ml_parser/html_parser';
import {removeWhitespaces} from '../ml_parser/html_whitespaces';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
import {NgModuleCompiler} from '../ng_module_compiler';
import {OutputEmitter} from '../output/abstract_emitter';
import * as o from '../output/output_ast';
import {ParseError} from '../parse_util';
import {compileNgModule as compileIvyModule} from '../render3/r3_module_compiler';
import {compilePipe as compileIvyPipe} from '../render3/r3_pipe_compiler';
import {compileComponent as compileIvyComponent, compileDirective as compileIvyDirective} from '../render3/r3_view_compiler';
import {HtmlToTemplateTransform} from '../render3/r3_template_transform';
import {compileComponent as compileIvyComponent, compileDirective as compileIvyDirective} from '../render3/r3_view_compiler_local';
import {DomElementSchemaRegistry} from '../schema/dom_element_schema_registry';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {BindingParser} from '../template_parser/binding_parser';
Expand All @@ -39,15 +43,11 @@ import {LazyRoute, listLazyRoutes, parseLazyRoute} from './lazy_routes';
import {PartialModule} from './partial_module';
import {StaticReflector} from './static_reflector';
import {StaticSymbol} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {createForJitStub, serializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, normalizeGenFileSuffix, splitTypescriptSuffix, summaryFileName, summaryForJitFileName, summaryForJitName} from './util';
import {ngfactoryFilePath, normalizeGenFileSuffix, splitTypescriptSuffix, summaryFileName, summaryForJitFileName} from './util';

enum StubEmitFlags {
Basic = 1 << 0,
TypeCheck = 1 << 1,
All = TypeCheck | Basic
}
const enum StubEmitFlags { Basic = 1 << 0, TypeCheck = 1 << 1, All = TypeCheck | Basic }

export class AotCompiler {
private _templateAstCache =
Expand Down Expand Up @@ -369,11 +369,12 @@ export class AotCompiler {
fileName: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: CompileNgModuleMetadata[],
injectables: CompileInjectableMetadata[], context: OutputContext): void {
const classes: o.ClassStmt[] = [];
const errors: ParseError[] = [];

const schemaRegistry = new DomElementSchemaRegistry();
const hostBindingParser = new BindingParser(
this._templateParser.expressionParser, DEFAULT_INTERPOLATION_CONFIG, null !, [], errors);
this._templateParser.expressionParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, [],
errors);

// Process all components and directives
directives.forEach(directiveType => {
Expand All @@ -384,11 +385,40 @@ export class AotCompiler {
error(
`Cannot determine the module for component '${identifierName(directiveMetadata.type)}'`);

const {template: parsedTemplate, pipes: parsedPipes} =
this._parseTemplate(directiveMetadata, module, module.transitiveModule.directives);
let htmlAst = directiveMetadata.template !.htmlAst !;
const preserveWhitespaces = directiveMetadata !.template !.preserveWhitespaces;

if (!preserveWhitespaces) {
htmlAst = removeWhitespaces(htmlAst);
}
const transform = new HtmlToTemplateTransform(hostBindingParser);
const nodes = html.visitAll(transform, htmlAst.rootNodes, null);
const hasNgContent = transform.hasNgContent;
const ngContentSelectors = transform.ngContentSelectors;

// Map of StaticType by directive selectors
const directiveTypeBySel = new Map<string, any>();

const directives = module.transitiveModule.directives.map(
dir => this._metadataResolver.getDirectiveSummary(dir.reference));

directives.forEach(directive => {
if (directive.selector) {
directiveTypeBySel.set(directive.selector, directive.type.reference);
}
});

// Map of StaticType by pipe names
const pipeTypeByName = new Map<string, any>();

const pipes = module.transitiveModule.pipes.map(
pipe => this._metadataResolver.getPipeSummary(pipe.reference));

pipes.forEach(pipe => { pipeTypeByName.set(pipe.name, pipe.type.reference); });

compileIvyComponent(
context, directiveMetadata, parsedPipes, parsedTemplate, this.reflector,
hostBindingParser);
context, directiveMetadata, nodes, hasNgContent, ngContentSelectors, this.reflector,
hostBindingParser, directiveTypeBySel, pipeTypeByName);
} else {
compileIvyDirective(context, directiveMetadata, this.reflector, hostBindingParser);
}
Expand Down Expand Up @@ -891,16 +921,13 @@ export function analyzeFileForInjectables(
if (!symbolMeta || symbolMeta.__symbolic === 'error') {
return;
}
let isNgSymbol = false;
if (symbolMeta.__symbolic === 'class') {
if (metadataResolver.isInjectable(symbol)) {
isNgSymbol = true;
const injectable = metadataResolver.getInjectableMetadata(symbol, null, false);
if (injectable) {
injectables.push(injectable);
}
} else if (metadataResolver.isNgModule(symbol)) {
isNgSymbol = true;
const module = metadataResolver.getShallowModuleMetadata(symbol);
if (module) {
shallowModules.push(module);
Expand Down
241 changes: 241 additions & 0 deletions packages/compiler/src/render3/r3_ast.ts
@@ -0,0 +1,241 @@
/**
* @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 {SecurityContext} from '../core';
import {AST} from '../expression_parser/ast';
import {ParseSourceSpan} from '../parse_util';

export interface Node {
sourceSpan: ParseSourceSpan;
visit<Result>(visitor: Visitor<Result>): Result;
}

export class Text implements Node {
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitText(this); }
}

export class BoundText implements Node {
constructor(public value: AST, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundText(this); }
}

export class TextAttribute implements Node {
constructor(
public name: string, public value: string, public sourceSpan: ParseSourceSpan,
public valueSpan?: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitAttribute(this); }
}

/**
* Enumeration of types of property bindings.
*/
export enum PropertyBindingType {

/**
* A normal binding to a property (e.g. `[property]="expression"`).
*/
Property,

/**
* A binding to an element attribute (e.g. `[attr.name]="expression"`).
*/
Attribute,

/**
* A binding to a CSS class (e.g. `[class.name]="condition"`).
*/
Class,

/**
* A binding to a style rule (e.g. `[style.rule]="expression"`).
*/
Style,

/**
* A binding to an animation reference (e.g. `[animate.key]="expression"`).
*/
Animation
}

export class BoundAttribute implements Node {
constructor(
public name: string, public type: PropertyBindingType,
public securityContext: SecurityContext, public value: AST, public unit: string|null,
public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundAttribute(this); }
}

export class BoundEvent implements Node {
constructor(
public name: string, public handler: AST, public target: string|null,
public phase: string|null, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundEvent(this); }
}

export class Element implements Node {
constructor(
public name: string, public attributes: TextAttribute[], public inputs: BoundAttribute[],
public outputs: BoundEvent[], public children: Node[], public references: Reference[],
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null,
public endSourceSpan: ParseSourceSpan|null) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitElement(this); }
}

export class Template implements Node {
constructor(
public attributes: TextAttribute[], public inputs: BoundAttribute[], public children: Node[],
public references: Reference[], public variables: Variable[],
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null,
public endSourceSpan: ParseSourceSpan|null) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitTemplate(this); }
}

export class Content implements Node {
constructor(
public selectorIndex: number, public attributes: TextAttribute[],
public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitContent(this); }
}

export class Variable implements Node {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitVariable(this); }
}

export class Reference implements Node {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitReference(this); }
}

export interface Visitor<Result = any> {
// Returning a truthy value from `visit()` will prevent `visitAll()` from the call to the typed
// method and result returned will become the result included in `visitAll()`s result array.
visit?(node: Node): Result;

visitElement(element: Element): Result;
visitTemplate(template: Template): Result;
visitContent(content: Content): Result;
visitVariable(variable: Variable): Result;
visitReference(reference: Reference): Result;
visitAttribute(attribute: TextAttribute): Result;
visitBoundAttribute(attribute: BoundAttribute): Result;
visitBoundEvent(attribute: BoundEvent): Result;
visitText(text: Text): Result;
visitBoundText(text: BoundText): Result;
}

export class NullVisitor implements Visitor<void> {
visitElement(element: Element): void {}
visitTemplate(template: Template): void {}
visitContent(content: Content): void {}
visitVariable(variable: Variable): void {}
visitReference(reference: Reference): void {}
visitAttribute(attribute: TextAttribute): void {}
visitBoundAttribute(attribute: BoundAttribute): void {}
visitBoundEvent(attribute: BoundEvent): void {}
visitText(text: Text): void {}
visitBoundText(text: BoundText): void {}
}

export class RecursiveVisitor implements Visitor<void> {
visitElement(element: Element): void {
visitAll(this, element.attributes);
visitAll(this, element.children);
visitAll(this, element.references);
}
visitTemplate(template: Template): void {
visitAll(this, template.attributes);
visitAll(this, template.children);
visitAll(this, template.references);
visitAll(this, template.variables);
}
visitContent(content: Content): void {}
visitVariable(variable: Variable): void {}
visitReference(reference: Reference): void {}
visitAttribute(attribute: TextAttribute): void {}
visitBoundAttribute(attribute: BoundAttribute): void {}
visitBoundEvent(attribute: BoundEvent): void {}
visitText(text: Text): void {}
visitBoundText(text: BoundText): void {}
}

export class TransformVisitor implements Visitor<Node> {
visitElement(element: Element): Node {
const newAttributes = transformAll(this, element.attributes);
const newInputs = transformAll(this, element.inputs);
const newOutputs = transformAll(this, element.outputs);
const newChildren = transformAll(this, element.children);
const newReferences = transformAll(this, element.references);
if (newAttributes != element.attributes || newInputs != element.inputs ||
newOutputs != element.outputs || newChildren != element.children ||
newReferences != element.references) {
return new Element(
element.name, newAttributes, newInputs, newOutputs, newChildren, newReferences,
element.sourceSpan, element.startSourceSpan, element.endSourceSpan);
}
return element;
}

visitTemplate(template: Template): Node {
const newAttributes = transformAll(this, template.attributes);
const newInputs = transformAll(this, template.inputs);
const newChildren = transformAll(this, template.children);
const newReferences = transformAll(this, template.references);
const newVariables = transformAll(this, template.variables);
if (newAttributes != template.attributes || newInputs != template.inputs ||
newChildren != template.children || newVariables != template.variables ||
newReferences != template.references) {
return new Template(
newAttributes, newInputs, newChildren, newReferences, newVariables, template.sourceSpan,
template.startSourceSpan, template.endSourceSpan);
}
return template;
}

visitContent(content: Content): Node { return content; }

visitVariable(variable: Variable): Node { return variable; }
visitReference(reference: Reference): Node { return reference; }
visitAttribute(attribute: TextAttribute): Node { return attribute; }
visitBoundAttribute(attribute: BoundAttribute): Node { return attribute; }
visitBoundEvent(attribute: BoundEvent): Node { return attribute; }
visitText(text: Text): Node { return text; }
visitBoundText(text: BoundText): Node { return text; }
}

export function visitAll<Result>(visitor: Visitor<Result>, nodes: Node[]): Result[] {
const result: Result[] = [];
if (visitor.visit) {
for (const node of nodes) {
const newNode = visitor.visit(node) || node.visit(visitor);
}
} else {
for (const node of nodes) {
const newNode = node.visit(visitor);
if (newNode) {
result.push(newNode);
}
}
}
return result;
}

export function transformAll<Result extends Node>(
visitor: Visitor<Node>, nodes: Result[]): Result[] {
const result: Result[] = [];
let changed = false;
for (const node of nodes) {
const newNode = node.visit(visitor);
if (newNode) {
result.push(newNode as Result);
}
changed = changed || newNode != node;
}
return changed ? result : nodes;
}
10 changes: 0 additions & 10 deletions packages/compiler/src/render3/r3_identifiers.ts
Expand Up @@ -31,14 +31,8 @@ export class Identifiers {

static containerCreate: o.ExternalReference = {name: 'ɵC', moduleName: CORE};

static containerEnd: o.ExternalReference = {name: 'ɵc', moduleName: CORE};

static directiveCreate: o.ExternalReference = {name: 'ɵD', moduleName: CORE};

static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};

static directiveInput: o.ExternalReference = {name: 'ɵi', moduleName: CORE};

static textCreateBound: o.ExternalReference = {name: 'ɵt', moduleName: CORE};

static bind: o.ExternalReference = {name: 'ɵb', moduleName: CORE};
Expand Down Expand Up @@ -77,10 +71,6 @@ export class Identifiers {
static projection: o.ExternalReference = {name: 'ɵP', moduleName: CORE};
static projectionDef: o.ExternalReference = {name: 'ɵpD', moduleName: CORE};

static refreshComponent: o.ExternalReference = {name: 'ɵr', moduleName: CORE};

static directiveLifeCycle: o.ExternalReference = {name: 'ɵl', moduleName: CORE};

static injectAttribute: o.ExternalReference = {name: 'ɵinjectAttribute', moduleName: CORE};

static injectElementRef: o.ExternalReference = {name: 'ɵinjectElementRef', moduleName: CORE};
Expand Down

0 comments on commit 20bd15a

Please sign in to comment.