refactor(compiler): move static_reflector
into @angular/compiler
and rename files
- `src/runtime_compiler.ts` -> `src/jit/compiler.ts` - `src/compiler.ts` -> `src/jit/compiler_factory.ts` - `src/offline_compiler` -> `src/aot/compiler.ts` Part of #12867
This commit is contained in:

committed by
Chuck Jazdzewski

parent
664a6273e1
commit
912ca44979
400
modules/@angular/compiler/src/aot/compiler.ts
Normal file
400
modules/@angular/compiler/src/aot/compiler.ts
Normal file
@ -0,0 +1,400 @@
|
||||
/**
|
||||
* @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 {SchemaMetadata} from '@angular/core';
|
||||
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
import {AnimationParser} from '../animation/animation_parser';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, createHostComponentMeta} from '../compile_metadata';
|
||||
import {DirectiveNormalizer} from '../directive_normalizer';
|
||||
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
import {NgModuleCompiler} from '../ng_module_compiler';
|
||||
import {OutputEmitter} from '../output/abstract_emitter';
|
||||
import * as o from '../output/output_ast';
|
||||
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
|
||||
import {TemplateParser} from '../template_parser/template_parser';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
||||
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
|
||||
export class SourceModule {
|
||||
constructor(public fileUrl: string, public moduleUrl: string, public source: string) {}
|
||||
}
|
||||
|
||||
export interface NgAnalyzedModules {
|
||||
ngModules: CompileNgModuleMetadata[];
|
||||
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;
|
||||
files: Array<{srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}>;
|
||||
symbolsMissingModule?: StaticSymbol[];
|
||||
}
|
||||
|
||||
// Returns all the source files and a mapping from modules to directives
|
||||
export function analyzeNgModules(
|
||||
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
||||
const {ngModules, symbolsMissingModule} =
|
||||
_createNgModules(programStaticSymbols, options, metadataResolver);
|
||||
return _analyzeNgModules(ngModules, symbolsMissingModule);
|
||||
}
|
||||
|
||||
|
||||
export function analyzeAndValidateNgModules(
|
||||
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||
metadataResolver: CompileMetadataResolver): NgAnalyzedModules {
|
||||
const result = analyzeNgModules(programStaticSymbols, options, metadataResolver);
|
||||
if (result.symbolsMissingModule && result.symbolsMissingModule.length) {
|
||||
const messages = result.symbolsMissingModule.map(
|
||||
s => `Cannot determine the module for class ${s.name} in ${s.filePath}!`);
|
||||
throw new Error(messages.join('\n'));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Wait for the directives in the given modules have been loaded
|
||||
export function loadNgModuleDirectives(ngModules: CompileNgModuleMetadata[]) {
|
||||
return Promise
|
||||
.all(ListWrapper.flatten(ngModules.map(
|
||||
(ngModule) => ngModule.transitiveModule.directiveLoaders.map(loader => loader()))))
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
function _analyzeNgModules(
|
||||
ngModuleMetas: CompileNgModuleMetadata[],
|
||||
symbolsMissingModule: StaticSymbol[]): NgAnalyzedModules {
|
||||
const moduleMetasByRef = new Map<any, CompileNgModuleMetadata>();
|
||||
ngModuleMetas.forEach((ngModule) => moduleMetasByRef.set(ngModule.type.reference, ngModule));
|
||||
const ngModuleByPipeOrDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||
const ngModulesByFile = new Map<string, StaticSymbol[]>();
|
||||
const ngDirectivesByFile = new Map<string, StaticSymbol[]>();
|
||||
const filePaths = new Set<string>();
|
||||
|
||||
// Looping over all modules to construct:
|
||||
// - a map from file to modules `ngModulesByFile`,
|
||||
// - a map from file to directives `ngDirectivesByFile`,
|
||||
// - a map from directive/pipe to module `ngModuleByPipeOrDirective`.
|
||||
ngModuleMetas.forEach((ngModuleMeta) => {
|
||||
const srcFileUrl = ngModuleMeta.type.reference.filePath;
|
||||
filePaths.add(srcFileUrl);
|
||||
ngModulesByFile.set(
|
||||
srcFileUrl, (ngModulesByFile.get(srcFileUrl) || []).concat(ngModuleMeta.type.reference));
|
||||
|
||||
ngModuleMeta.declaredDirectives.forEach((dirIdentifier) => {
|
||||
const fileUrl = dirIdentifier.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngDirectivesByFile.set(
|
||||
fileUrl, (ngDirectivesByFile.get(fileUrl) || []).concat(dirIdentifier.reference));
|
||||
ngModuleByPipeOrDirective.set(dirIdentifier.reference, ngModuleMeta);
|
||||
});
|
||||
ngModuleMeta.declaredPipes.forEach((pipeIdentifier) => {
|
||||
const fileUrl = pipeIdentifier.reference.filePath;
|
||||
filePaths.add(fileUrl);
|
||||
ngModuleByPipeOrDirective.set(pipeIdentifier.reference, ngModuleMeta);
|
||||
});
|
||||
});
|
||||
|
||||
const files: {srcUrl: string, directives: StaticSymbol[], ngModules: StaticSymbol[]}[] = [];
|
||||
|
||||
filePaths.forEach((srcUrl) => {
|
||||
const directives = ngDirectivesByFile.get(srcUrl) || [];
|
||||
const ngModules = ngModulesByFile.get(srcUrl) || [];
|
||||
files.push({srcUrl, directives, ngModules});
|
||||
});
|
||||
|
||||
return {
|
||||
// map directive/pipe to module
|
||||
ngModuleByPipeOrDirective,
|
||||
// list modules and directives for every source file
|
||||
files,
|
||||
ngModules: ngModuleMetas, symbolsMissingModule
|
||||
};
|
||||
}
|
||||
|
||||
export class AotCompiler {
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(
|
||||
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
|
||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||
private _localeId: string, private _translationFormat: string,
|
||||
private _animationParser: AnimationParser) {}
|
||||
|
||||
clearCache() { this._metadataResolver.clearCache(); }
|
||||
|
||||
compileModules(staticSymbols: StaticSymbol[], options: {transitiveModules: boolean}):
|
||||
Promise<SourceModule[]> {
|
||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||
analyzeAndValidateNgModules(staticSymbols, options, this._metadataResolver);
|
||||
return loadNgModuleDirectives(ngModules).then(() => {
|
||||
const sourceModules = files.map(
|
||||
file => this._compileSrcFile(
|
||||
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.ngModules));
|
||||
return ListWrapper.flatten(sourceModules);
|
||||
});
|
||||
}
|
||||
|
||||
private _compileSrcFile(
|
||||
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||
directives: StaticSymbol[], ngModules: StaticSymbol[]): SourceModule[] {
|
||||
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
|
||||
const statements: o.Statement[] = [];
|
||||
const exportedVars: string[] = [];
|
||||
const outputSourceModules: SourceModule[] = [];
|
||||
|
||||
// compile all ng modules
|
||||
exportedVars.push(
|
||||
...ngModules.map((ngModuleType) => this._compileModule(ngModuleType, statements)));
|
||||
|
||||
// compile directive wrappers
|
||||
exportedVars.push(...directives.map(
|
||||
(directiveType) => this._compileDirectiveWrapper(directiveType, statements)));
|
||||
|
||||
// compile components
|
||||
directives.forEach((dirType) => {
|
||||
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
|
||||
if (!compMeta.isComponent) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
const ngModule = ngModuleByPipeOrDirective.get(dirType);
|
||||
if (!ngModule) {
|
||||
throw new Error(
|
||||
`Internal Error: cannot determine the module for component ${compMeta.type.name}!`);
|
||||
}
|
||||
|
||||
_assertComponent(compMeta);
|
||||
|
||||
// compile styles
|
||||
const stylesCompileResults = this._styleCompiler.compileComponent(compMeta);
|
||||
stylesCompileResults.externalStylesheets.forEach((compiledStyleSheet) => {
|
||||
outputSourceModules.push(this._codgenStyles(srcFileUrl, compiledStyleSheet, fileSuffix));
|
||||
});
|
||||
|
||||
// compile components
|
||||
exportedVars.push(
|
||||
this._compileComponentFactory(compMeta, ngModule, fileSuffix, statements),
|
||||
this._compileComponent(
|
||||
compMeta, ngModule, ngModule.transitiveModule.directives,
|
||||
stylesCompileResults.componentStylesheet, fileSuffix, statements));
|
||||
});
|
||||
if (statements.length > 0) {
|
||||
const srcModule = this._codegenSourceModule(
|
||||
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
|
||||
outputSourceModules.unshift(srcModule);
|
||||
}
|
||||
return outputSourceModules;
|
||||
}
|
||||
|
||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||
const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType);
|
||||
const providers: CompileProviderMetadata[] = [];
|
||||
|
||||
if (this._localeId) {
|
||||
providers.push(new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.LOCALE_ID),
|
||||
useValue: this._localeId,
|
||||
}));
|
||||
}
|
||||
|
||||
if (this._translationFormat) {
|
||||
providers.push(new CompileProviderMetadata({
|
||||
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
||||
useValue: this._translationFormat
|
||||
}));
|
||||
}
|
||||
|
||||
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
||||
|
||||
appCompileResult.dependencies.forEach((dep) => {
|
||||
dep.placeholder.name = _componentFactoryName(dep.comp);
|
||||
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
|
||||
});
|
||||
|
||||
targetStatements.push(...appCompileResult.statements);
|
||||
return appCompileResult.ngModuleFactoryVar;
|
||||
}
|
||||
|
||||
private _compileDirectiveWrapper(directiveType: StaticSymbol, targetStatements: o.Statement[]):
|
||||
string {
|
||||
const dirMeta = this._metadataResolver.getDirectiveMetadata(directiveType);
|
||||
const dirCompileResult = this._dirWrapperCompiler.compile(dirMeta);
|
||||
|
||||
targetStatements.push(...dirCompileResult.statements);
|
||||
return dirCompileResult.dirWrapperClassVar;
|
||||
}
|
||||
|
||||
private _compileComponentFactory(
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
|
||||
targetStatements: o.Statement[]): string {
|
||||
const hostMeta = createHostComponentMeta(compMeta);
|
||||
const hostViewFactoryVar = this._compileComponent(
|
||||
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
|
||||
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||
targetStatements.push(
|
||||
o.variable(compFactoryVar)
|
||||
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
|
||||
compMeta.type)])
|
||||
.instantiate(
|
||||
[
|
||||
o.literal(compMeta.selector),
|
||||
o.variable(hostViewFactoryVar),
|
||||
o.importExpr(compMeta.type),
|
||||
],
|
||||
o.importType(
|
||||
resolveIdentifier(Identifiers.ComponentFactory),
|
||||
[o.importType(compMeta.type)], [o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
return compFactoryVar;
|
||||
}
|
||||
|
||||
private _compileComponent(
|
||||
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
|
||||
directiveIdentifiers: CompileIdentifierMetadata[], componentStyles: CompiledStylesheet,
|
||||
fileSuffix: string, targetStatements: o.Statement[]): string {
|
||||
const parsedAnimations = this._animationParser.parseComponent(compMeta);
|
||||
const directives =
|
||||
directiveIdentifiers.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference));
|
||||
const pipes = ngModule.transitiveModule.pipes.map(
|
||||
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
|
||||
|
||||
const parsedTemplate = this._templateParser.parse(
|
||||
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
|
||||
compMeta.type.name);
|
||||
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||
const compiledAnimations =
|
||||
this._animationCompiler.compile(compMeta.type.name, parsedAnimations);
|
||||
const viewResult = this._viewCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
|
||||
if (componentStyles) {
|
||||
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
|
||||
}
|
||||
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
|
||||
targetStatements.push(..._resolveViewStatements(viewResult));
|
||||
return viewResult.viewClassVar;
|
||||
}
|
||||
|
||||
private _codgenStyles(
|
||||
fileUrl: string, stylesCompileResult: CompiledStylesheet, fileSuffix: string): SourceModule {
|
||||
_resolveStyleStatements(stylesCompileResult, fileSuffix);
|
||||
return this._codegenSourceModule(
|
||||
fileUrl, _stylesModuleUrl(
|
||||
stylesCompileResult.meta.moduleUrl, stylesCompileResult.isShimmed, fileSuffix),
|
||||
stylesCompileResult.statements, [stylesCompileResult.stylesVar]);
|
||||
}
|
||||
|
||||
private _codegenSourceModule(
|
||||
fileUrl: string, moduleUrl: string, statements: o.Statement[],
|
||||
exportedVars: string[]): SourceModule {
|
||||
return new SourceModule(
|
||||
fileUrl, moduleUrl,
|
||||
this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
|
||||
}
|
||||
}
|
||||
|
||||
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
if (dep instanceof ViewClassDependency) {
|
||||
const vfd = <ViewClassDependency>dep;
|
||||
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
|
||||
} else if (dep instanceof ComponentFactoryDependency) {
|
||||
const cfd = <ComponentFactoryDependency>dep;
|
||||
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
||||
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
||||
} else if (dep instanceof DirectiveWrapperDependency) {
|
||||
const dwd = <DirectiveWrapperDependency>dep;
|
||||
dwd.placeholder.moduleUrl = _ngfactoryModuleUrl(dwd.dir.moduleUrl);
|
||||
}
|
||||
});
|
||||
return compileResult.statements;
|
||||
}
|
||||
|
||||
|
||||
function _resolveStyleStatements(
|
||||
compileResult: CompiledStylesheet, fileSuffix: string): o.Statement[] {
|
||||
compileResult.dependencies.forEach((dep) => {
|
||||
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.moduleUrl, dep.isShimmed, fileSuffix);
|
||||
});
|
||||
return compileResult.statements;
|
||||
}
|
||||
|
||||
function _ngfactoryModuleUrl(dirUrl: string): string {
|
||||
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
|
||||
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
||||
}
|
||||
|
||||
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
|
||||
return `${comp.name}NgFactory`;
|
||||
}
|
||||
|
||||
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
|
||||
return shim ? `${stylesheetUrl}.shim${suffix}` : `${stylesheetUrl}${suffix}`;
|
||||
}
|
||||
|
||||
function _assertComponent(meta: CompileDirectiveMetadata) {
|
||||
if (!meta.isComponent) {
|
||||
throw new Error(`Could not compile '${meta.type.name}' because it is not a component.`);
|
||||
}
|
||||
}
|
||||
|
||||
function _splitTypescriptSuffix(path: string): string[] {
|
||||
if (path.endsWith('.d.ts')) {
|
||||
return [path.slice(0, -5), '.ts'];
|
||||
}
|
||||
|
||||
const lastDot = path.lastIndexOf('.');
|
||||
|
||||
if (lastDot !== -1) {
|
||||
return [path.substring(0, lastDot), path.substring(lastDot)];
|
||||
}
|
||||
|
||||
return [path, ''];
|
||||
}
|
||||
|
||||
// Load the NgModules and check
|
||||
// that all directives / pipes that are present in the program
|
||||
// are also declared by a module.
|
||||
function _createNgModules(
|
||||
programStaticSymbols: StaticSymbol[], options: {transitiveModules: boolean},
|
||||
metadataResolver: CompileMetadataResolver):
|
||||
{ngModules: CompileNgModuleMetadata[], symbolsMissingModule: StaticSymbol[]} {
|
||||
const ngModules = new Map<any, CompileNgModuleMetadata>();
|
||||
const programPipesAndDirectives: StaticSymbol[] = [];
|
||||
const ngModulePipesAndDirective = new Set<StaticSymbol>();
|
||||
|
||||
const addNgModule = (staticSymbol: any) => {
|
||||
if (ngModules.has(staticSymbol)) {
|
||||
return false;
|
||||
}
|
||||
const ngModule = metadataResolver.getUnloadedNgModuleMetadata(staticSymbol, false, false);
|
||||
if (ngModule) {
|
||||
ngModules.set(ngModule.type.reference, ngModule);
|
||||
ngModule.declaredDirectives.forEach((dir) => ngModulePipesAndDirective.add(dir.reference));
|
||||
ngModule.declaredPipes.forEach((pipe) => ngModulePipesAndDirective.add(pipe.reference));
|
||||
if (options.transitiveModules) {
|
||||
// For every input modules add the list of transitively included modules
|
||||
ngModule.transitiveModule.modules.forEach(modMeta => addNgModule(modMeta.type.reference));
|
||||
}
|
||||
}
|
||||
return !!ngModule;
|
||||
};
|
||||
programStaticSymbols.forEach((staticSymbol) => {
|
||||
if (!addNgModule(staticSymbol) &&
|
||||
(metadataResolver.isDirective(staticSymbol) || metadataResolver.isPipe(staticSymbol))) {
|
||||
programPipesAndDirectives.push(staticSymbol);
|
||||
}
|
||||
});
|
||||
|
||||
// Throw an error if any of the program pipe or directives is not declared by a module
|
||||
const symbolsMissingModule =
|
||||
programPipesAndDirectives.filter(s => !ngModulePipesAndDirective.has(s));
|
||||
|
||||
return {ngModules: Array.from(ngModules.values()), symbolsMissingModule};
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @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 {GetterFn, MethodFn, ReflectionCapabilities, SetterFn, reflector} from '../private_import_core';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
|
||||
export class StaticAndDynamicReflectionCapabilities {
|
||||
static install(staticDelegate: StaticReflector) {
|
||||
reflector.updateCapabilities(new StaticAndDynamicReflectionCapabilities(staticDelegate));
|
||||
}
|
||||
|
||||
private dynamicDelegate = new ReflectionCapabilities();
|
||||
|
||||
constructor(private staticDelegate: StaticReflector) {}
|
||||
|
||||
isReflectionEnabled(): boolean { return true; }
|
||||
factory(type: any): Function { return this.dynamicDelegate.factory(type); }
|
||||
|
||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||
return isStaticType(type) ? this.staticDelegate.hasLifecycleHook(type, lcProperty) :
|
||||
this.dynamicDelegate.hasLifecycleHook(type, lcProperty);
|
||||
}
|
||||
parameters(type: any): any[][] {
|
||||
return isStaticType(type) ? this.staticDelegate.parameters(type) :
|
||||
this.dynamicDelegate.parameters(type);
|
||||
}
|
||||
annotations(type: any): any[] {
|
||||
return isStaticType(type) ? this.staticDelegate.annotations(type) :
|
||||
this.dynamicDelegate.annotations(type);
|
||||
}
|
||||
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||
return isStaticType(typeOrFunc) ? this.staticDelegate.propMetadata(typeOrFunc) :
|
||||
this.dynamicDelegate.propMetadata(typeOrFunc);
|
||||
}
|
||||
getter(name: string): GetterFn { return this.dynamicDelegate.getter(name); }
|
||||
setter(name: string): SetterFn { return this.dynamicDelegate.setter(name); }
|
||||
method(name: string): MethodFn { return this.dynamicDelegate.method(name); }
|
||||
importUri(type: any): string { return this.staticDelegate.importUri(type); }
|
||||
resolveIdentifier(name: string, moduleUrl: string, runtime: any) {
|
||||
return this.staticDelegate.resolveIdentifier(name, moduleUrl, runtime);
|
||||
}
|
||||
resolveEnum(enumIdentifier: any, name: string): any {
|
||||
if (isStaticType(enumIdentifier)) {
|
||||
return this.staticDelegate.resolveEnum(enumIdentifier, name);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isStaticType(type: any): boolean {
|
||||
return typeof type === 'object' && type.name && type.filePath;
|
||||
}
|
667
modules/@angular/compiler/src/aot/static_reflector.ts
Normal file
667
modules/@angular/compiler/src/aot/static_reflector.ts
Normal file
@ -0,0 +1,667 @@
|
||||
/**
|
||||
* @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 {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
|
||||
import {ReflectorReader} from '../private_import_core';
|
||||
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
|
||||
const SUPPORTED_SCHEMA_VERSION = 1;
|
||||
|
||||
/**
|
||||
* 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 given module.
|
||||
*
|
||||
* @param modulePath is a string identifier for a module as an absolute path.
|
||||
* @returns the metadata for the given module.
|
||||
*/
|
||||
getMetadataFor(modulePath: string): {[key: string]: any}|{[key: string]: any}[];
|
||||
|
||||
/**
|
||||
* Resolve a symbol from an import statement form, to the file where it is declared.
|
||||
* @param module the location imported from
|
||||
* @param containingFile for relative imports, the path of the file containing the import
|
||||
*/
|
||||
findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol;
|
||||
|
||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol;
|
||||
|
||||
angularImportLocations(): {
|
||||
coreDecorators: string,
|
||||
diDecorators: string,
|
||||
diMetadata: string,
|
||||
diOpaqueToken: string,
|
||||
animationMetadata: string,
|
||||
provider: string
|
||||
};
|
||||
|
||||
getCanonicalFileName(fileName: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A static reflector implements enough of the Reflector API that is necessary to compile
|
||||
* templates statically.
|
||||
*/
|
||||
export class StaticReflector implements ReflectorReader {
|
||||
private annotationCache = new Map<StaticSymbol, any[]>();
|
||||
private propertyCache = new Map<StaticSymbol, {[key: string]: any}>();
|
||||
private parameterCache = new Map<StaticSymbol, any[]>();
|
||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||
private opaqueToken: StaticSymbol;
|
||||
|
||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||
|
||||
importUri(typeOrFunc: StaticSymbol): string {
|
||||
const staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
||||
return staticSymbol ? staticSymbol.filePath : null;
|
||||
}
|
||||
|
||||
resolveIdentifier(name: string, moduleUrl: string, runtime: any): any {
|
||||
return this.host.findDeclaration(moduleUrl, name, '');
|
||||
}
|
||||
|
||||
resolveEnum(enumIdentifier: any, name: string): any {
|
||||
const staticSymbol: StaticSymbol = enumIdentifier;
|
||||
return this.host.getStaticSymbol(staticSymbol.filePath, staticSymbol.name, [name]);
|
||||
}
|
||||
|
||||
public annotations(type: StaticSymbol): any[] {
|
||||
let annotations = this.annotationCache.get(type);
|
||||
if (!annotations) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
if (classMetadata['decorators']) {
|
||||
annotations = this.simplify(type, classMetadata['decorators']);
|
||||
} else {
|
||||
annotations = [];
|
||||
}
|
||||
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
let propMetadata = this.propertyCache.get(type);
|
||||
if (!propMetadata) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : {};
|
||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||
const prop = (<any[]>propData)
|
||||
.find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
||||
if (prop && prop['decorators']) {
|
||||
return this.simplify(type, prop['decorators']);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
this.propertyCache.set(type, propMetadata);
|
||||
}
|
||||
return propMetadata;
|
||||
}
|
||||
|
||||
public parameters(type: StaticSymbol): any[] {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
throw new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
||||
}
|
||||
try {
|
||||
let parameters = this.parameterCache.get(type);
|
||||
if (!parameters) {
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : null;
|
||||
const ctorData = members ? members['__ctor__'] : null;
|
||||
if (ctorData) {
|
||||
const ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
||||
const parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
||||
const parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
||||
parameters = [];
|
||||
parameterTypes.forEach((paramType, index) => {
|
||||
const nestedResult: any[] = [];
|
||||
if (paramType) {
|
||||
nestedResult.push(paramType);
|
||||
}
|
||||
const decorators = parameterDecorators ? parameterDecorators[index] : null;
|
||||
if (decorators) {
|
||||
nestedResult.push(...decorators);
|
||||
}
|
||||
parameters.push(nestedResult);
|
||||
});
|
||||
}
|
||||
if (!parameters) {
|
||||
parameters = [];
|
||||
}
|
||||
this.parameterCache.set(type, parameters);
|
||||
}
|
||||
return parameters;
|
||||
} catch (e) {
|
||||
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
hasLifecycleHook(type: any, lcProperty: string): boolean {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
throw new Error(
|
||||
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
||||
}
|
||||
const classMetadata = this.getTypeMetadata(type);
|
||||
const members = classMetadata ? classMetadata['members'] : null;
|
||||
const member: any[] =
|
||||
members && members.hasOwnProperty(lcProperty) ? members[lcProperty] : null;
|
||||
return member ? member.some(a => a['__symbolic'] == 'method') : false;
|
||||
}
|
||||
|
||||
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => new ctor(...args));
|
||||
}
|
||||
|
||||
private registerFunction(type: StaticSymbol, fn: any): void {
|
||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => fn.apply(undefined, args));
|
||||
}
|
||||
|
||||
private initializeConversionMap(): void {
|
||||
const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} =
|
||||
this.host.angularImportLocations();
|
||||
this.opaqueToken = this.host.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
||||
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Host'), Host);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Injectable'), Injectable);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Self'), Self);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'SkipSelf'), SkipSelf);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Inject'), Inject);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Optional'), Optional);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Attribute'), Attribute);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChild'), ContentChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildren);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewChild'), ViewChild);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildren);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Input'), Input);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Output'), Output);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(coreDecorators, 'Pipe'), Pipe);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'HostBinding'), HostBinding);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'HostListener'), HostListener);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Directive'), Directive);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Component'), Component);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'NgModule'), NgModule);
|
||||
|
||||
// Note: Some metadata classes can be used directly with Provider.deps.
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Host'), Host);
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'Self'), Self);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'SkipSelf'), SkipSelf);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'Optional'), Optional);
|
||||
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'trigger'), trigger);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'state'), state);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'transition'), transition);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'style'), style);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'animate'), animate);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'sequence'), sequence);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'group'), group);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public simplify(context: StaticSymbol, value: any): any {
|
||||
const _this = this;
|
||||
let scope = BindingScope.empty;
|
||||
const calling = new Map<StaticSymbol, boolean>();
|
||||
|
||||
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
||||
function resolveReference(context: StaticSymbol, expression: any): StaticSymbol {
|
||||
let staticSymbol: StaticSymbol;
|
||||
if (expression['module']) {
|
||||
staticSymbol = _this.host.findDeclaration(
|
||||
expression['module'], expression['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, expression['name']);
|
||||
}
|
||||
return staticSymbol;
|
||||
}
|
||||
|
||||
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
|
||||
const moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
||||
const declarationValue =
|
||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||
return declarationValue;
|
||||
}
|
||||
|
||||
function isOpaqueToken(context: StaticSymbol, value: any): boolean {
|
||||
if (value && value.__symbolic === 'new' && value.expression) {
|
||||
const target = value.expression;
|
||||
if (target.__symbolic == 'reference') {
|
||||
return sameSymbol(resolveReference(context, target), _this.opaqueToken);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function simplifyCall(expression: any) {
|
||||
let callContext: {[name: string]: string}|undefined = undefined;
|
||||
if (expression['__symbolic'] == 'call') {
|
||||
const target = expression['expression'];
|
||||
let functionSymbol: StaticSymbol;
|
||||
let targetFunction: any;
|
||||
if (target) {
|
||||
switch (target.__symbolic) {
|
||||
case 'reference':
|
||||
// Find the function to call.
|
||||
callContext = {name: target.name};
|
||||
functionSymbol = resolveReference(context, target);
|
||||
targetFunction = resolveReferenceValue(functionSymbol);
|
||||
break;
|
||||
case 'select':
|
||||
// Find the static method to call
|
||||
if (target.expression.__symbolic == 'reference') {
|
||||
functionSymbol = resolveReference(context, target.expression);
|
||||
const classData = resolveReferenceValue(functionSymbol);
|
||||
if (classData && classData.statics) {
|
||||
targetFunction = classData.statics[target.member];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (targetFunction && targetFunction['__symbolic'] == 'function') {
|
||||
if (calling.get(functionSymbol)) {
|
||||
throw new Error('Recursion not supported');
|
||||
}
|
||||
calling.set(functionSymbol, true);
|
||||
try {
|
||||
const value = targetFunction['value'];
|
||||
if (value && (depth != 0 || value.__symbolic != 'error')) {
|
||||
// Determine the arguments
|
||||
const args: any[] =
|
||||
(expression['arguments'] || []).map((arg: any) => simplify(arg));
|
||||
const parameters: string[] = targetFunction['parameters'];
|
||||
const defaults: any[] = targetFunction.defaults;
|
||||
if (defaults && defaults.length > args.length) {
|
||||
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
|
||||
}
|
||||
const functionScope = BindingScope.build();
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
functionScope.define(parameters[i], args[i]);
|
||||
}
|
||||
const oldScope = scope;
|
||||
let result: any;
|
||||
try {
|
||||
scope = functionScope.done();
|
||||
result = simplifyInContext(functionSymbol, value, depth + 1);
|
||||
} finally {
|
||||
scope = oldScope;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} finally {
|
||||
calling.delete(functionSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (depth === 0) {
|
||||
// If depth is 0 we are evaluating the top level expression that is describing element
|
||||
// decorator. In this case, it is a decorator we don't understand, such as a custom
|
||||
// non-angular decorator, and we should just ignore it.
|
||||
return {__symbolic: 'ignore'};
|
||||
}
|
||||
return simplify(
|
||||
{__symbolic: 'error', message: 'Function call not supported', context: callContext});
|
||||
}
|
||||
|
||||
function simplify(expression: any): any {
|
||||
if (isPrimitive(expression)) {
|
||||
return expression;
|
||||
}
|
||||
if (expression instanceof Array) {
|
||||
const result: any[] = [];
|
||||
for (const item of (<any>expression)) {
|
||||
// Check for a spread expression
|
||||
if (item && item.__symbolic === 'spread') {
|
||||
const spreadArray = simplify(item.expression);
|
||||
if (Array.isArray(spreadArray)) {
|
||||
for (const spreadItem of spreadArray) {
|
||||
result.push(spreadItem);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const value = simplify(item);
|
||||
if (shouldIgnore(value)) {
|
||||
continue;
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (expression instanceof StaticSymbol) {
|
||||
return expression;
|
||||
}
|
||||
if (expression) {
|
||||
if (expression['__symbolic']) {
|
||||
let staticSymbol: StaticSymbol;
|
||||
switch (expression['__symbolic']) {
|
||||
case 'binop':
|
||||
let left = simplify(expression['left']);
|
||||
if (shouldIgnore(left)) return left;
|
||||
let right = simplify(expression['right']);
|
||||
if (shouldIgnore(right)) return 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 'if':
|
||||
let condition = simplify(expression['condition']);
|
||||
return condition ? simplify(expression['thenExpression']) :
|
||||
simplify(expression['elseExpression']);
|
||||
case 'pre':
|
||||
let operand = simplify(expression['operand']);
|
||||
if (shouldIgnore(operand)) return 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 (indexTarget && isPrimitive(index)) return indexTarget[index];
|
||||
return null;
|
||||
case 'select':
|
||||
let selectTarget = simplify(expression['expression']);
|
||||
if (selectTarget instanceof StaticSymbol) {
|
||||
// Access to a static instance variable
|
||||
const declarationValue = resolveReferenceValue(selectTarget);
|
||||
if (declarationValue && declarationValue.statics) {
|
||||
selectTarget = declarationValue.statics;
|
||||
} else {
|
||||
const member: string = expression['member'];
|
||||
const members = selectTarget.members ?
|
||||
(selectTarget.members as string[]).concat(member) :
|
||||
[member];
|
||||
return _this.host.getStaticSymbol(
|
||||
selectTarget.filePath, selectTarget.name, members);
|
||||
}
|
||||
}
|
||||
const member = simplify(expression['member']);
|
||||
if (selectTarget && isPrimitive(member)) return simplify(selectTarget[member]);
|
||||
return null;
|
||||
case 'reference':
|
||||
if (!expression.module) {
|
||||
const name: string = expression['name'];
|
||||
const localValue = scope.resolve(name);
|
||||
if (localValue != BindingScope.missing) {
|
||||
return localValue;
|
||||
}
|
||||
}
|
||||
staticSymbol = resolveReference(context, expression);
|
||||
let result: any = staticSymbol;
|
||||
let declarationValue = resolveReferenceValue(result);
|
||||
if (declarationValue) {
|
||||
if (isOpaqueToken(staticSymbol, declarationValue)) {
|
||||
// If the referenced symbol is initalized by a new OpaqueToken we can keep the
|
||||
// reference to the symbol.
|
||||
return staticSymbol;
|
||||
}
|
||||
result = simplifyInContext(staticSymbol, declarationValue, depth + 1);
|
||||
}
|
||||
return result;
|
||||
case 'class':
|
||||
return context;
|
||||
case 'function':
|
||||
return context;
|
||||
case 'new':
|
||||
case 'call':
|
||||
// Determine if the function is a built-in conversion
|
||||
let target = expression['expression'];
|
||||
if (target['module']) {
|
||||
staticSymbol = _this.host.findDeclaration(
|
||||
target['module'], target['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, target['name']);
|
||||
}
|
||||
let converter = _this.conversionMap.get(staticSymbol);
|
||||
if (converter) {
|
||||
let args: any[] = expression['arguments'];
|
||||
if (!args) {
|
||||
args = [];
|
||||
}
|
||||
return converter(
|
||||
context, args.map(arg => simplifyInContext(context, arg, depth + 1)));
|
||||
}
|
||||
|
||||
// Determine if the function is one we can simplify.
|
||||
return simplifyCall(expression);
|
||||
|
||||
case 'error':
|
||||
let message = produceErrorMessage(expression);
|
||||
if (expression['line']) {
|
||||
message =
|
||||
`${message} (position ${expression['line']+1}:${expression['character']+1} in the original .ts file)`;
|
||||
throw positionalError(
|
||||
message, context.filePath, expression['line'], expression['character']);
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return mapStringMap(expression, (value, name) => simplify(value));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return simplify(value);
|
||||
} catch (e) {
|
||||
const message = `${e.message}, resolving symbol ${context.name} in ${context.filePath}`;
|
||||
if (e.fileName) {
|
||||
throw positionalError(message, e.fileName, e.line, e.column);
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
const result = simplifyInContext(context, value, 0);
|
||||
if (shouldIgnore(result)) {
|
||||
return undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param module an absolute path to a module file.
|
||||
*/
|
||||
public getModuleMetadata(module: string): {[key: string]: any} {
|
||||
let moduleMetadata = this.metadataCache.get(module);
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata = this.host.getMetadataFor(module);
|
||||
if (Array.isArray(moduleMetadata)) {
|
||||
moduleMetadata = moduleMetadata.find(md => md['version'] === SUPPORTED_SCHEMA_VERSION) ||
|
||||
moduleMetadata[0];
|
||||
}
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata =
|
||||
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
||||
}
|
||||
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
||||
throw new Error(
|
||||
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`);
|
||||
}
|
||||
this.metadataCache.set(module, moduleMetadata);
|
||||
}
|
||||
return moduleMetadata;
|
||||
}
|
||||
|
||||
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
const moduleMetadata = this.getModuleMetadata(type.filePath);
|
||||
return moduleMetadata['metadata'][type.name] || {__symbolic: 'class'};
|
||||
}
|
||||
}
|
||||
|
||||
function expandedMessage(error: any): string {
|
||||
switch (error.message) {
|
||||
case 'Reference to non-exported class':
|
||||
if (error.context && error.context.className) {
|
||||
return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`;
|
||||
}
|
||||
break;
|
||||
case 'Variable not initialized':
|
||||
return 'Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler';
|
||||
case 'Destructuring not supported':
|
||||
return 'Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring';
|
||||
case 'Could not resolve type':
|
||||
if (error.context && error.context.typeName) {
|
||||
return `Could not resolve type ${error.context.typeName}`;
|
||||
}
|
||||
break;
|
||||
case 'Function call not supported':
|
||||
let prefix =
|
||||
error.context && error.context.name ? `Calling function '${error.context.name}', f` : 'F';
|
||||
return prefix +
|
||||
'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
|
||||
case 'Reference to a local symbol':
|
||||
if (error.context && error.context.name) {
|
||||
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
||||
}
|
||||
}
|
||||
return error.message;
|
||||
}
|
||||
|
||||
function produceErrorMessage(error: any): string {
|
||||
return `Error encountered resolving symbol values statically. ${expandedMessage(error)}`;
|
||||
}
|
||||
|
||||
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
|
||||
{[key: string]: any} {
|
||||
if (!input) return {};
|
||||
const result: {[key: string]: any} = {};
|
||||
Object.keys(input).forEach((key) => {
|
||||
const value = transform(input[key], key);
|
||||
if (!shouldIgnore(value)) {
|
||||
result[key] = value;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function isPrimitive(o: any): boolean {
|
||||
return o === null || (typeof o !== 'function' && typeof o !== 'object');
|
||||
}
|
||||
|
||||
interface BindingScopeBuilder {
|
||||
define(name: string, value: any): BindingScopeBuilder;
|
||||
done(): BindingScope;
|
||||
}
|
||||
|
||||
abstract class BindingScope {
|
||||
abstract resolve(name: string): any;
|
||||
public static missing = {};
|
||||
public static empty: BindingScope = {resolve: name => BindingScope.missing};
|
||||
|
||||
public static build(): BindingScopeBuilder {
|
||||
const current = new Map<string, any>();
|
||||
return {
|
||||
define: function(name, value) {
|
||||
current.set(name, value);
|
||||
return this;
|
||||
},
|
||||
done: function() {
|
||||
return current.size > 0 ? new PopulatedScope(current) : BindingScope.empty;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class PopulatedScope extends BindingScope {
|
||||
constructor(private bindings: Map<string, any>) { super(); }
|
||||
|
||||
resolve(name: string): any {
|
||||
return this.bindings.has(name) ? this.bindings.get(name) : BindingScope.missing;
|
||||
}
|
||||
}
|
||||
|
||||
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
||||
return a === b || (a.name == b.name && a.filePath == b.filePath);
|
||||
}
|
||||
|
||||
function shouldIgnore(value: any): boolean {
|
||||
return value && value.__symbolic == 'ignore';
|
||||
}
|
||||
|
||||
function positionalError(message: string, fileName: string, line: number, column: number): Error {
|
||||
const result = new Error(message);
|
||||
(result as any).fileName = fileName;
|
||||
(result as any).line = line;
|
||||
(result as any).column = column;
|
||||
return result;
|
||||
}
|
20
modules/@angular/compiler/src/aot/static_symbol.ts
Normal file
20
modules/@angular/compiler/src/aot/static_symbol.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
export function isStaticSymbol(value: any): value is StaticSymbol {
|
||||
return typeof value === 'object' && value !== null && value['name'] && value['filePath'];
|
||||
}
|
||||
|
||||
/**
|
||||
* A token representing the a reference to a static type.
|
||||
*
|
||||
* This token is unique for a filePath and name and can be used as a hash table key.
|
||||
*/
|
||||
export class StaticSymbol {
|
||||
constructor(public filePath: string, public name: string, public members?: string[]) {}
|
||||
}
|
Reference in New Issue
Block a user