feat(compiler): do not evaluate metadata expressions that can use references (#18001)

This commit is contained in:
Chuck Jazdzewski
2017-07-13 17:16:56 -06:00
committed by Igor Minar
parent 72143e80da
commit ddb766e456
10 changed files with 264 additions and 42 deletions

View File

@ -18,6 +18,7 @@ import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
const ANGULAR_CORE = '@angular/core';
const ANGULAR_ROUTER = '@angular/router';
const HIDDEN_KEY = /^\$.*\$$/;
@ -25,6 +26,10 @@ const IGNORE = {
__symbolic: 'ignore'
};
const USE_VALUE = 'useValue';
const PROVIDE = 'provide';
const REFERENCE_SET = new Set([USE_VALUE, 'useFactory', 'data']);
function shouldIgnore(value: any): boolean {
return value && value.__symbolic == 'ignore';
}
@ -41,6 +46,8 @@ export class StaticReflector implements CompileReflector {
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private injectionToken: StaticSymbol;
private opaqueToken: StaticSymbol;
private ROUTES: StaticSymbol;
private ANALYZE_FOR_ENTRY_COMPONENTS: StaticSymbol;
private annotationForParentClassWithSummaryKind = new Map<CompileSummaryKind, any[]>();
private annotationNames = new Map<any, string>();
@ -88,6 +95,10 @@ export class StaticReflector implements CompileReflector {
this.symbolResolver.getSymbolByModule(moduleUrl, name, containingFile));
}
tryFindDeclaration(moduleUrl: string, name: string): StaticSymbol {
return this.symbolResolver.ignoreErrorsFor(() => this.findDeclaration(moduleUrl, name));
}
findSymbolDeclaration(symbol: StaticSymbol): StaticSymbol {
const resolvedSymbol = this.symbolResolver.resolveSymbol(symbol);
if (resolvedSymbol && resolvedSymbol.metadata instanceof StaticSymbol) {
@ -267,6 +278,9 @@ export class StaticReflector implements CompileReflector {
private initializeConversionMap(): void {
this.injectionToken = this.findDeclaration(ANGULAR_CORE, 'InjectionToken');
this.opaqueToken = this.findDeclaration(ANGULAR_CORE, 'OpaqueToken');
this.ROUTES = this.tryFindDeclaration(ANGULAR_ROUTER, 'ROUTES');
this.ANALYZE_FOR_ENTRY_COMPONENTS =
this.findDeclaration(ANGULAR_CORE, 'ANALYZE_FOR_ENTRY_COMPONENTS');
this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Host'), Host);
this._registerDecoratorOrConstructor(
@ -350,7 +364,8 @@ export class StaticReflector implements CompileReflector {
let scope = BindingScope.empty;
const calling = new Map<StaticSymbol, boolean>();
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
function simplifyInContext(
context: StaticSymbol, value: any, depth: number, references: number): any {
function resolveReferenceValue(staticSymbol: StaticSymbol): any {
const resolvedSymbol = self.symbolResolver.resolveSymbol(staticSymbol);
return resolvedSymbol ? resolvedSymbol.metadata : null;
@ -367,7 +382,7 @@ export class StaticReflector implements CompileReflector {
if (value && (depth != 0 || value.__symbolic != 'error')) {
const parameters: string[] = targetFunction['parameters'];
const defaults: any[] = targetFunction.defaults;
args = args.map(arg => simplifyInContext(context, arg, depth + 1))
args = args.map(arg => simplifyInContext(context, arg, depth + 1, references))
.map(arg => shouldIgnore(arg) ? undefined : arg);
if (defaults && defaults.length > args.length) {
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
@ -380,7 +395,7 @@ export class StaticReflector implements CompileReflector {
let result: any;
try {
scope = functionScope.done();
result = simplifyInContext(functionSymbol, value, depth + 1);
result = simplifyInContext(functionSymbol, value, depth + 1, references);
} finally {
scope = oldScope;
}
@ -427,15 +442,15 @@ export class StaticReflector implements CompileReflector {
return result;
}
if (expression instanceof StaticSymbol) {
// Stop simplification at builtin symbols
// Stop simplification at builtin symbols or if we are in a reference context
if (expression === self.injectionToken || expression === self.opaqueToken ||
self.conversionMap.has(expression)) {
self.conversionMap.has(expression) || references > 0) {
return expression;
} else {
const staticSymbol = expression;
const declarationValue = resolveReferenceValue(staticSymbol);
if (declarationValue) {
return simplifyInContext(staticSymbol, declarationValue, depth + 1);
return simplifyInContext(staticSymbol, declarationValue, depth + 1, references);
} else {
return staticSymbol;
}
@ -526,13 +541,15 @@ export class StaticReflector implements CompileReflector {
self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
const declarationValue = resolveReferenceValue(selectContext);
if (declarationValue) {
return simplifyInContext(selectContext, declarationValue, depth + 1);
return simplifyInContext(
selectContext, declarationValue, depth + 1, references);
} else {
return selectContext;
}
}
if (selectTarget && isPrimitive(member))
return simplifyInContext(selectContext, selectTarget[member], depth + 1);
return simplifyInContext(
selectContext, selectTarget[member], depth + 1, references);
return null;
case 'reference':
// Note: This only has to deal with variable references,
@ -551,7 +568,8 @@ export class StaticReflector implements CompileReflector {
case 'new':
case 'call':
// Determine if the function is a built-in conversion
staticSymbol = simplifyInContext(context, expression['expression'], depth + 1);
staticSymbol = simplifyInContext(
context, expression['expression'], depth + 1, /* references */ 0);
if (staticSymbol instanceof StaticSymbol) {
if (staticSymbol === self.injectionToken || staticSymbol === self.opaqueToken) {
// if somebody calls new InjectionToken, don't create an InjectionToken,
@ -562,7 +580,8 @@ export class StaticReflector implements CompileReflector {
let converter = self.conversionMap.get(staticSymbol);
if (converter) {
const args =
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1))
argExpressions
.map(arg => simplifyInContext(context, arg, depth + 1, references))
.map(arg => shouldIgnore(arg) ? undefined : arg);
return converter(context, args);
} else {
@ -590,7 +609,20 @@ export class StaticReflector implements CompileReflector {
}
return null;
}
return mapStringMap(expression, (value, name) => simplify(value));
return mapStringMap(expression, (value, name) => {
if (REFERENCE_SET.has(name)) {
if (name === USE_VALUE && PROVIDE in expression) {
// If this is a provider expression, check for special tokens that need the value
// during analysis.
const provide = simplify(expression.provide);
if (provide === self.ROUTES || provide == self.ANALYZE_FOR_ENTRY_COMPONENTS) {
return simplify(value);
}
}
return simplifyInContext(context, value, depth, references + 1);
}
return simplify(value)
});
}
return IGNORE;
}
@ -608,16 +640,16 @@ export class StaticReflector implements CompileReflector {
}
}
const recordedSimplifyInContext = (context: StaticSymbol, value: any, depth: number) => {
const recordedSimplifyInContext = (context: StaticSymbol, value: any) => {
try {
return simplifyInContext(context, value, depth);
return simplifyInContext(context, value, 0, 0);
} catch (e) {
this.reportError(e, context);
}
};
const result = this.errorRecorder ? recordedSimplifyInContext(context, value, 0) :
simplifyInContext(context, value, 0);
const result = this.errorRecorder ? recordedSimplifyInContext(context, value) :
simplifyInContext(context, value, 0, 0);
if (shouldIgnore(result)) {
return undefined;
}

View File

@ -191,6 +191,17 @@ export class StaticSymbolResolver {
}
}
/* @internal */
ignoreErrorsFor<T>(cb: () => T) {
const recorder = this.errorRecorder;
this.errorRecorder = () => {};
try {
return cb();
} finally {
this.errorRecorder = recorder;
}
}
private _resolveSymbolMembers(staticSymbol: StaticSymbol): ResolvedStaticSymbol|null {
const members = staticSymbol.members;
const baseResolvedSymbol =
@ -446,6 +457,7 @@ export class StaticSymbolResolver {
return moduleMetadata;
}
getSymbolByModule(module: string, symbolName: string, containingFile?: string): StaticSymbol {
const filePath = this.resolveModule(module, containingFile);
if (!filePath) {