feat(compiler): do not evaluate metadata expressions that can use references (#18001)
This commit is contained in:

committed by
Igor Minar

parent
72143e80da
commit
ddb766e456
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user