Skip to content

Commit

Permalink
refactor(compiler): allow sync AOT compilation.
Browse files Browse the repository at this point in the history
AOT compilation can be executed synchronously now,
if the `ReosurceLoader` returns a string directly
(and no `Promise`).
  • Loading branch information
tbosch committed May 22, 2017
1 parent b7a711a commit 20cde8a
Show file tree
Hide file tree
Showing 15 changed files with 623 additions and 689 deletions.
2 changes: 1 addition & 1 deletion packages/compiler-cli/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class CodeGenerator {

codegen(): Promise<any> {
return this.compiler
.compileAll(this.program.getSourceFiles().map(
.compileAllAsync(this.program.getSourceFiles().map(
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
.then(generatedModules => {
generatedModules.forEach(generatedModule => {
Expand Down
8 changes: 6 additions & 2 deletions packages/compiler-cli/test/diagnostics/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import {DiagnosticTemplateInfo} from '../../src/diagnostics/expression_diagnosti
import {getClassFromStaticSymbol, getClassMembers, getPipesTable, getSymbolQuery} from '../../src/diagnostics/typescript_symbols';
import {Directory, MockAotContext} from '../mocks';

const packages = path.join(__dirname, '../../../../../packages');
function calcRootPath() {
const moduleFilename = module.filename.replace(/\\/g, '/');
const distIndex = moduleFilename.indexOf('/dist/all');
return moduleFilename.substr(0, distIndex);
}

const realFiles = new Map<string, string>();

Expand All @@ -40,7 +44,7 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost, Compiler
strictNullChecks: true,
baseUrl: currentDirectory,
lib: ['lib.es2015.d.ts', 'lib.dom.d.ts'],
paths: {'@angular/*': [packages + '/*']}
paths: {'@angular/*': [calcRootPath() + '/packages/*']}
};
this.context = new MockAotContext(currentDirectory, files)
}
Expand Down
16 changes: 15 additions & 1 deletion packages/compiler/src/aot/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class AotCompiler {

clearCache() { this._metadataResolver.clearCache(); }

compileAll(rootFiles: string[]): Promise<GeneratedFile[]> {
compileAllAsync(rootFiles: string[]): Promise<GeneratedFile[]> {
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
Expand All @@ -56,6 +56,20 @@ export class AotCompiler {
});
}

compileAllSync(rootFiles: string[]): GeneratedFile[] {
const programSymbols = extractProgramSymbols(this._symbolResolver, rootFiles, this._host);
const {ngModuleByPipeOrDirective, files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this._host, this._metadataResolver);
ngModules.forEach(
ngModule => this._metadataResolver.loadNgModuleDirectiveAndPipeMetadata(
ngModule.type.reference, true));
const sourceModules = files.map(
file => this._compileSrcFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes, file.ngModules,
file.injectables));
return flatten(sourceModules);
}

private _compileSrcFile(
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/aot/compiler_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryRes
/**
* Loads a resource (e.g. html / css)
*/
loadResource(path: string): Promise<string>;
loadResource(path: string): Promise<string>|string;
}
103 changes: 50 additions & 53 deletions packages/compiler/src/directive_normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {ResourceLoader} from './resource_loader';
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
import {PreparsedElementType, preparseElement} from './template_parser/template_preparser';
import {UrlResolver} from './url_resolver';
import {SyncAsyncResult, isDefined, syntaxError} from './util';
import {SyncAsync, isDefined, syntaxError} from './util';

export interface PrenormalizedTemplateMetadata {
ngModuleType: any;
Expand All @@ -35,7 +35,7 @@ export interface PrenormalizedTemplateMetadata {

@CompilerInjectable()
export class DirectiveNormalizer {
private _resourceLoaderCache = new Map<string, Promise<string>>();
private _resourceLoaderCache = new Map<string, SyncAsync<string>>();

constructor(
private _resourceLoader: ResourceLoader, private _urlResolver: UrlResolver,
Expand All @@ -53,19 +53,17 @@ export class DirectiveNormalizer {
(stylesheet) => { this._resourceLoaderCache.delete(stylesheet.moduleUrl !); });
}

private _fetch(url: string): Promise<string> {
private _fetch(url: string): SyncAsync<string> {
let result = this._resourceLoaderCache.get(url);
if (!result) {
result = this._resourceLoader.get(url) !;
result = this._resourceLoader.get(url);
this._resourceLoaderCache.set(url, result);
}
return result;
}

normalizeTemplate(prenormData: PrenormalizedTemplateMetadata):
SyncAsyncResult<CompileTemplateMetadata> {
let normalizedTemplateSync: CompileTemplateMetadata = null !;
let normalizedTemplateAsync: Promise<CompileTemplateMetadata> = undefined !;
SyncAsync<CompileTemplateMetadata> {
if (isDefined(prenormData.template)) {
if (isDefined(prenormData.templateUrl)) {
throw syntaxError(
Expand All @@ -75,39 +73,33 @@ export class DirectiveNormalizer {
throw syntaxError(
`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
}
normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync !);
} else if (isDefined(prenormData.templateUrl)) {
if (typeof prenormData.templateUrl !== 'string') {
throw syntaxError(
`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
}
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
} else {
throw syntaxError(
`No template specified for component ${stringify(prenormData.componentType)}`);
}
return SyncAsync.then(
this.normalizeTemplateOnly(prenormData),
(result: CompileTemplateMetadata) => this.normalizeExternalStylesheets(result));
}

if (normalizedTemplateSync && normalizedTemplateSync.styleUrls.length === 0) {
// sync case
return new SyncAsyncResult(normalizedTemplateSync);
normalizeTemplateOnly(prenomData: PrenormalizedTemplateMetadata):
SyncAsync<CompileTemplateMetadata> {
let template: SyncAsync<string>;
let templateUrl: string;
if (prenomData.template != null) {
template = prenomData.template;
templateUrl = prenomData.moduleUrl;
} else {
// async case
return new SyncAsyncResult(
null, normalizedTemplateAsync.then(
(normalizedTemplate) => this.normalizeExternalStylesheets(normalizedTemplate)));
templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl !);
template = this._fetch(templateUrl);
}
}

normalizeTemplateSync(prenomData: PrenormalizedTemplateMetadata): CompileTemplateMetadata {
return this.normalizeLoadedTemplate(prenomData, prenomData.template !, prenomData.moduleUrl);
}

normalizeTemplateAsync(prenomData: PrenormalizedTemplateMetadata):
Promise<CompileTemplateMetadata> {
const templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl !);
return this._fetch(templateUrl)
.then((value) => this.normalizeLoadedTemplate(prenomData, value, templateUrl));
return SyncAsync.then(
template, (template) => this.normalizeLoadedTemplate(prenomData, template, templateUrl));
}

normalizeLoadedTemplate(
Expand Down Expand Up @@ -162,37 +154,42 @@ export class DirectiveNormalizer {
}

normalizeExternalStylesheets(templateMeta: CompileTemplateMetadata):
Promise<CompileTemplateMetadata> {
return this._loadMissingExternalStylesheets(templateMeta.styleUrls)
.then((externalStylesheets) => new CompileTemplateMetadata({
encapsulation: templateMeta.encapsulation,
template: templateMeta.template,
templateUrl: templateMeta.templateUrl,
styles: templateMeta.styles,
styleUrls: templateMeta.styleUrls,
externalStylesheets: externalStylesheets,
ngContentSelectors: templateMeta.ngContentSelectors,
animations: templateMeta.animations,
interpolation: templateMeta.interpolation,
isInline: templateMeta.isInline,
}));
SyncAsync<CompileTemplateMetadata> {
return SyncAsync.then(
this._loadMissingExternalStylesheets(templateMeta.styleUrls),
(externalStylesheets) => new CompileTemplateMetadata({
encapsulation: templateMeta.encapsulation,
template: templateMeta.template,
templateUrl: templateMeta.templateUrl,
styles: templateMeta.styles,
styleUrls: templateMeta.styleUrls,
externalStylesheets: externalStylesheets,
ngContentSelectors: templateMeta.ngContentSelectors,
animations: templateMeta.animations,
interpolation: templateMeta.interpolation,
isInline: templateMeta.isInline,
}));
}

private _loadMissingExternalStylesheets(
styleUrls: string[],
loadedStylesheets:
Map<string, CompileStylesheetMetadata> = new Map<string, CompileStylesheetMetadata>()):
Promise<CompileStylesheetMetadata[]> {
return Promise
.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
.map(styleUrl => this._fetch(styleUrl).then((loadedStyle) => {
const stylesheet = this.normalizeStylesheet(
new CompileStylesheetMetadata({styles: [loadedStyle], moduleUrl: styleUrl}));
loadedStylesheets.set(styleUrl, stylesheet);
return this._loadMissingExternalStylesheets(
stylesheet.styleUrls, loadedStylesheets);
})))
.then((_) => Array.from(loadedStylesheets.values()));
SyncAsync<CompileStylesheetMetadata[]> {
return SyncAsync.then(
SyncAsync.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
.map(
styleUrl => SyncAsync.then(
this._fetch(styleUrl),
(loadedStyle) => {
const stylesheet =
this.normalizeStylesheet(new CompileStylesheetMetadata(
{styles: [loadedStyle], moduleUrl: styleUrl}));
loadedStylesheets.set(styleUrl, stylesheet);
return this._loadMissingExternalStylesheets(
stylesheet.styleUrls, loadedStylesheets);
}))),
(_) => Array.from(loadedStylesheets.values()));
}

normalizeStylesheet(stylesheet: CompileStylesheetMetadata): CompileStylesheetMetadata {
Expand Down
42 changes: 15 additions & 27 deletions packages/compiler/src/jit/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {jitStatements} from '../output/output_jit';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {OutputContext, SyncAsyncResult} from '../util';
import {OutputContext, SyncAsync} from '../util';
import {ViewCompiler} from '../view_compiler/view_compiler';


Expand Down Expand Up @@ -51,20 +51,20 @@ export class JitCompiler implements Compiler {
get injector(): Injector { return this._injector; }

compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
return this._compileModuleAndComponents(moduleType, true).syncResult !;
return SyncAsync.assertSync(this._compileModuleAndComponents(moduleType, true));
}

compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>> {
return this._compileModuleAndComponents(moduleType, false).asyncResult !;
return Promise.resolve(this._compileModuleAndComponents(moduleType, false));
}

compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T> {
return this._compileModuleAndAllComponents(moduleType, true).syncResult !;
return SyncAsync.assertSync(this._compileModuleAndAllComponents(moduleType, true));
}

compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>):
Promise<ModuleWithComponentFactories<T>> {
return this._compileModuleAndAllComponents(moduleType, false).asyncResult !;
return Promise.resolve(this._compileModuleAndAllComponents(moduleType, false));
}

getNgContentSelectors(component: Type<any>): string[] {
Expand Down Expand Up @@ -97,36 +97,24 @@ export class JitCompiler implements Compiler {
}

private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
SyncAsyncResult<NgModuleFactory<T>> {
const loadingPromise = this._loadModules(moduleType, isSync);
const createResult = () => {
SyncAsync<NgModuleFactory<T>> {
return SyncAsync.then(this._loadModules(moduleType, isSync), () => {
this._compileComponents(moduleType, null);
return this._compileModule(moduleType);
};
if (isSync) {
return new SyncAsyncResult(createResult());
} else {
return new SyncAsyncResult(null, loadingPromise.then(createResult));
}
});
}

private _compileModuleAndAllComponents<T>(moduleType: Type<T>, isSync: boolean):
SyncAsyncResult<ModuleWithComponentFactories<T>> {
const loadingPromise = this._loadModules(moduleType, isSync);
const createResult = () => {
SyncAsync<ModuleWithComponentFactories<T>> {
return SyncAsync.then(this._loadModules(moduleType, isSync), () => {
const componentFactories: ComponentFactory<any>[] = [];
this._compileComponents(moduleType, componentFactories);
return new ModuleWithComponentFactories(this._compileModule(moduleType), componentFactories);
};
if (isSync) {
return new SyncAsyncResult(createResult());
} else {
return new SyncAsyncResult(null, loadingPromise.then(createResult));
}
});
}

private _loadModules(mainModule: any, isSync: boolean): Promise<any> {
const loadingPromises: Promise<any>[] = [];
private _loadModules(mainModule: any, isSync: boolean): SyncAsync<any> {
const loading: Promise<any>[] = [];
const mainNgModule = this._metadataResolver.getNgModuleMetadata(mainModule) !;
// Note: for runtime compilation, we want to transitively compile all modules,
// so we also need to load the declared directives / pipes for all nested modules.
Expand All @@ -137,13 +125,13 @@ export class JitCompiler implements Compiler {
const promise =
this._metadataResolver.loadDirectiveMetadata(moduleMeta.type.reference, ref, isSync);
if (promise) {
loadingPromises.push(promise);
loading.push(promise);
}
});
this._filterJitIdentifiers(moduleMeta.declaredPipes)
.forEach((ref) => this._metadataResolver.getOrLoadPipeMetadata(ref));
});
return Promise.all(loadingPromises);
return SyncAsync.all(loading);
}

private _compileModule<T>(moduleType: Type<T>): NgModuleFactory<T> {
Expand Down
19 changes: 7 additions & 12 deletions packages/compiler/src/metadata_resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererType2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵConsole as Console, ɵERROR_COMPONENT_TYPE, ɵccf as createComponentFactory, ɵstringify as stringify} from '@angular/core';
import {Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererType2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵConsole as Console, ɵERROR_COMPONENT_TYPE, ɵccf as createComponentFactory, ɵisPromise as isPromise, ɵstringify as stringify} from '@angular/core';

import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
import {ngfactoryFilePath} from './aot/util';
Expand All @@ -24,7 +24,7 @@ import {PipeResolver} from './pipe_resolver';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, ValueTransformer, noUndefined, syntaxError, visitValue} from './util';
import {MODULE_SUFFIX, SyncAsync, ValueTransformer, noUndefined, syntaxError, visitValue} from './util';

export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector');
Expand Down Expand Up @@ -170,7 +170,7 @@ export class CompileMetadataResolver {
return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
}

loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean): Promise<any>|null {
loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean): SyncAsync<null> {
if (this._directiveCache.has(directiveType)) {
return null;
}
Expand Down Expand Up @@ -205,7 +205,7 @@ export class CompileMetadataResolver {
}
this._directiveCache.set(directiveType, normalizedDirMeta);
this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
return normalizedDirMeta;
return null;
};

if (metadata.isComponent) {
Expand All @@ -222,16 +222,11 @@ export class CompileMetadataResolver {
animations: template.animations,
interpolation: template.interpolation
});
if (templateMeta.syncResult) {
createDirectiveMetadata(templateMeta.syncResult);
if (isPromise(templateMeta) && isSync) {
this._reportError(componentStillLoadingError(directiveType), directiveType);
return null;
} else {
if (isSync) {
this._reportError(componentStillLoadingError(directiveType), directiveType);
return null;
}
return templateMeta.asyncResult !.then(createDirectiveMetadata);
}
return SyncAsync.then(templateMeta, createDirectiveMetadata);
} else {
// directive
createDirectiveMetadata(null);
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/resource_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
* to load templates.
*/
export class ResourceLoader {
get(url: string): Promise<string>|null { return null; }
get(url: string): Promise<string>|string { return ''; }
}

0 comments on commit 20cde8a

Please sign in to comment.