This change marks all relevant define* callsites as pure, causing the compiler to emmit either @__PURE__ or @pureOrBreakMyCode annotation based on whether we are compiling code annotated for closure or terser. This change is needed in g3 where we don't run build optimizer but we need the code to be annotated for the closure compiler. Additionally this change allows for simplification of CLI and build optimizer as they will no longer need to rewrite the generated code (there are still other places where a build optimizer rewrite will be necessary so we can't remove it, we can only simplify it). PR Close #41096
138 lines
4.9 KiB
TypeScript
138 lines
4.9 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC 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 {StaticSymbol} from './aot/static_symbol';
|
|
import {CompileInjectableMetadata, CompileNgModuleMetadata, CompileProviderMetadata, identifierName} from './compile_metadata';
|
|
import {CompileReflector} from './compile_reflector';
|
|
import {InjectFlags, NodeFlags} from './core';
|
|
import {Identifiers} from './identifiers';
|
|
import * as o from './output/output_ast';
|
|
import {convertValueToOutputAst} from './output/value_util';
|
|
import {typeSourceSpan} from './parse_util';
|
|
import {NgModuleProviderAnalyzer} from './provider_analyzer';
|
|
import {OutputContext} from './util';
|
|
import {componentFactoryResolverProviderDef, depDef, providerDef} from './view_compiler/provider_compiler';
|
|
|
|
type MapEntry = {
|
|
key: string,
|
|
quoted: boolean,
|
|
value: o.Expression
|
|
};
|
|
type MapLiteral = MapEntry[];
|
|
|
|
function mapEntry(key: string, value: o.Expression): MapEntry {
|
|
return {key, value, quoted: false};
|
|
}
|
|
|
|
export class InjectableCompiler {
|
|
private tokenInjector: StaticSymbol;
|
|
constructor(private reflector: CompileReflector, private alwaysGenerateDef: boolean) {
|
|
this.tokenInjector = reflector.resolveExternalReference(Identifiers.Injector);
|
|
}
|
|
|
|
private depsArray(deps: any[], ctx: OutputContext): o.Expression[] {
|
|
return deps.map(dep => {
|
|
let token = dep;
|
|
let args = [token];
|
|
let flags: InjectFlags = InjectFlags.Default;
|
|
if (Array.isArray(dep)) {
|
|
for (let i = 0; i < dep.length; i++) {
|
|
const v = dep[i];
|
|
if (v) {
|
|
if (v.ngMetadataName === 'Optional') {
|
|
flags |= InjectFlags.Optional;
|
|
} else if (v.ngMetadataName === 'SkipSelf') {
|
|
flags |= InjectFlags.SkipSelf;
|
|
} else if (v.ngMetadataName === 'Self') {
|
|
flags |= InjectFlags.Self;
|
|
} else if (v.ngMetadataName === 'Inject') {
|
|
token = v.token;
|
|
} else {
|
|
token = v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let tokenExpr: o.Expression;
|
|
if (typeof token === 'string') {
|
|
tokenExpr = o.literal(token);
|
|
} else if (token === this.tokenInjector) {
|
|
tokenExpr = o.importExpr(Identifiers.INJECTOR);
|
|
} else {
|
|
tokenExpr = ctx.importExpr(token);
|
|
}
|
|
|
|
if (flags !== InjectFlags.Default) {
|
|
args = [tokenExpr, o.literal(flags)];
|
|
} else {
|
|
args = [tokenExpr];
|
|
}
|
|
return o.importExpr(Identifiers.inject).callFn(args);
|
|
});
|
|
}
|
|
|
|
factoryFor(injectable: CompileInjectableMetadata, ctx: OutputContext): o.Expression {
|
|
let retValue: o.Expression;
|
|
if (injectable.useExisting) {
|
|
retValue = o.importExpr(Identifiers.inject).callFn([ctx.importExpr(injectable.useExisting)]);
|
|
} else if (injectable.useFactory) {
|
|
const deps = injectable.deps || [];
|
|
if (deps.length > 0) {
|
|
retValue = ctx.importExpr(injectable.useFactory).callFn(this.depsArray(deps, ctx));
|
|
} else {
|
|
return ctx.importExpr(injectable.useFactory);
|
|
}
|
|
} else if (injectable.useValue) {
|
|
retValue = convertValueToOutputAst(ctx, injectable.useValue);
|
|
} else {
|
|
const clazz = injectable.useClass || injectable.symbol;
|
|
const depArgs = this.depsArray(this.reflector.parameters(clazz), ctx);
|
|
retValue = new o.InstantiateExpr(ctx.importExpr(clazz), depArgs);
|
|
}
|
|
return o.fn(
|
|
[], [new o.ReturnStatement(retValue)], undefined, undefined,
|
|
injectable.symbol.name + '_Factory');
|
|
}
|
|
|
|
injectableDef(injectable: CompileInjectableMetadata, ctx: OutputContext): o.Expression {
|
|
let providedIn: o.Expression = o.NULL_EXPR;
|
|
if (injectable.providedIn !== undefined) {
|
|
if (injectable.providedIn === null) {
|
|
providedIn = o.NULL_EXPR;
|
|
} else if (typeof injectable.providedIn === 'string') {
|
|
providedIn = o.literal(injectable.providedIn);
|
|
} else {
|
|
providedIn = ctx.importExpr(injectable.providedIn);
|
|
}
|
|
}
|
|
const def: MapLiteral = [
|
|
mapEntry('factory', this.factoryFor(injectable, ctx)),
|
|
mapEntry('token', ctx.importExpr(injectable.type.reference)),
|
|
mapEntry('providedIn', providedIn),
|
|
];
|
|
return o.importExpr(Identifiers.ɵɵdefineInjectable)
|
|
.callFn([o.literalMap(def)], undefined, true);
|
|
}
|
|
|
|
compile(injectable: CompileInjectableMetadata, ctx: OutputContext): void {
|
|
if (this.alwaysGenerateDef || injectable.providedIn !== undefined) {
|
|
const className = identifierName(injectable.type)!;
|
|
const clazz = new o.ClassStmt(
|
|
className, null,
|
|
[
|
|
new o.ClassField(
|
|
'ɵprov', o.INFERRED_TYPE, [o.StmtModifier.Static],
|
|
this.injectableDef(injectable, ctx)),
|
|
],
|
|
[], new o.ClassMethod(null, [], []), []);
|
|
ctx.statements.push(clazz);
|
|
}
|
|
}
|
|
}
|