Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(compiler): reexport less symbols in .ngfactory.ts files #19884

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/compiler-cli/src/transformers/lower_expressions.ts
Expand Up @@ -6,7 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/

import {createLoweredSymbol, isLoweredSymbol} from '@angular/compiler';
import * as ts from 'typescript';

import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata, isMetadataGlobalReferenceExpression} from '../metadata/index';

export interface LoweringRequest {
Expand Down Expand Up @@ -223,14 +225,12 @@ function shouldLower(node: ts.Node | undefined): boolean {
return true;
}

const REWRITE_PREFIX = '\u0275';

function isPrimitive(value: any): boolean {
return Object(value) !== value;
}

function isRewritten(value: any): boolean {
return isMetadataGlobalReferenceExpression(value) && value.name.startsWith(REWRITE_PREFIX);
return isMetadataGlobalReferenceExpression(value) && isLoweredSymbol(value.name);
}

function isLiteralFieldNamed(node: ts.Node, names: Set<string>): boolean {
Expand Down Expand Up @@ -274,7 +274,7 @@ export class LowerMetadataCache implements RequestsMap {

private getMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
let identNumber = 0;
const freshIdent = () => REWRITE_PREFIX + identNumber++;
const freshIdent = () => createLoweredSymbol(identNumber++);
const requests = new Map<number, LoweringRequest>();

const isExportedSymbol = (() => {
Expand Down
8 changes: 2 additions & 6 deletions packages/compiler/src/aot/summary_resolver.ts
Expand Up @@ -10,7 +10,7 @@ import {Summary, SummaryResolver} from '../summary_resolver';

import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {deserializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, stripGeneratedFileSuffix, summaryFileName} from './util';
import {stripGeneratedFileSuffix, summaryFileName} from './util';

export interface AotSummaryResolverHost {
/**
Expand Down Expand Up @@ -119,11 +119,7 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
if (moduleName) {
this.knownFileNameToModuleNames.set(filePath, moduleName);
}
importAs.forEach((importAs) => {
this.importAs.set(
importAs.symbol,
this.staticSymbolCache.get(ngfactoryFilePath(filePath), importAs.importAs));
});
importAs.forEach((importAs) => { this.importAs.set(importAs.symbol, importAs.importAs); });
}
return hasSummary;
}
Expand Down
127 changes: 90 additions & 37 deletions packages/compiler/src/aot/summary_serializer.ts
Expand Up @@ -12,7 +12,7 @@ import {OutputContext, ValueTransformer, ValueVisitor, visitValue} from '../util

import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {summaryForJitFileName, summaryForJitName} from './util';
import {isLoweredSymbol, ngfactoryFilePath, summaryForJitFileName, summaryForJitName} from './util';

export function serializeSummaries(
srcFileName: string, forJitCtx: OutputContext | null,
Expand All @@ -38,7 +38,7 @@ export function serializeSummaries(
});
const {json, exportAs} = toJsonSerializer.serialize();
if (forJitCtx) {
const forJitSerializer = new ForJitSerializer(forJitCtx, symbolResolver);
const forJitSerializer = new ForJitSerializer(forJitCtx, symbolResolver, summaryResolver);
types.forEach(({summary, metadata}) => { forJitSerializer.addSourceType(summary, metadata); });
toJsonSerializer.unprocessedSymbolSummariesBySymbol.forEach((summary) => {
if (summaryResolver.isLibraryFile(summary.symbol.filePath) && summary.type) {
Expand All @@ -55,7 +55,7 @@ export function deserializeSummaries(
libraryFileName: string, json: string): {
moduleName: string | null,
summaries: Summary<StaticSymbol>[],
importAs: {symbol: StaticSymbol, importAs: string}[]
importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[]
} {
const deserializer = new FromJsonDeserializer(symbolCache, summaryResolver);
return deserializer.deserialize(libraryFileName, json);
Expand Down Expand Up @@ -83,6 +83,7 @@ class ToJsonSerializer extends ValueTransformer {
// Note: This only contains symbols without members.
private symbols: StaticSymbol[] = [];
private indexBySymbol = new Map<StaticSymbol, number>();
private reexportedBy = new Map<StaticSymbol, StaticSymbol>();
// This now contains a `__symbol: number` in the place of
// StaticSymbols, but otherwise has the same shape as the original objects.
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
Expand Down Expand Up @@ -126,9 +127,32 @@ class ToJsonSerializer extends ValueTransformer {
}
});
metadata = clone;
} else if (isCall(metadata)) {
if (!isFunctionCall(metadata) && !isMethodCallOnVariable(metadata)) {
// Don't store complex calls as we won't be able to simplify them anyways later on.
metadata = {
__symbolic: 'error',
message: 'Complex function calls are not supported.',
};
}
}
// Note: We need to keep storing ctor calls for e.g.
// `export const x = new InjectionToken(...)`
unprocessedSummary.metadata = metadata;
processedSummary.metadata = this.processValue(metadata, SerializationFlags.ResolveValue);
if (metadata instanceof StaticSymbol &&
this.summaryResolver.isLibraryFile(metadata.filePath)) {
const declarationSymbol = this.symbols[this.indexBySymbol.get(metadata) !];
if (!isLoweredSymbol(declarationSymbol.name)) {
// Note: symbols that were introduced during codegen in the user file can have a reexport
// if a user used `export *`. However, we can't rely on this as tsickle will change
// `export *` into named exports, using only the information from the typechecker.
// As we introduce the new symbols after typecheck, Tsickle does not know about them,
// and omits them when expanding `export *`.
// So we have to keep reexporting these symbols manually via .ngfactory files.
this.reexportedBy.set(declarationSymbol, summary.symbol);
}
}
}
if (!unprocessedSummary.type && summary.type) {
unprocessedSummary.type = summary.type;
Expand Down Expand Up @@ -161,12 +185,17 @@ class ToJsonSerializer extends ValueTransformer {
summaries: this.processedSummaries,
symbols: this.symbols.map((symbol, index) => {
symbol.assertNoMembers();
let importAs: string = undefined !;
let importAs: string|number = undefined !;
if (this.summaryResolver.isLibraryFile(symbol.filePath)) {
const summary = this.unprocessedSymbolSummariesBySymbol.get(symbol);
if (!summary || !summary.metadata || summary.metadata.__symbolic !== 'interface') {
importAs = `${symbol.name}_${index}`;
exportAs.push({symbol, exportAs: importAs});
const reexportSymbol = this.reexportedBy.get(symbol);
if (reexportSymbol) {
importAs = this.indexBySymbol.get(reexportSymbol) !;
} else {
const summary = this.unprocessedSymbolSummariesBySymbol.get(symbol);
if (!summary || !summary.metadata || summary.metadata.__symbolic !== 'interface') {
importAs = `${symbol.name}_${index}`;
exportAs.push({symbol, exportAs: importAs});
}
}
}
return {
Expand Down Expand Up @@ -246,54 +275,61 @@ class ToJsonSerializer extends ValueTransformer {
}

class ForJitSerializer {
private data = new Map<StaticSymbol, {
private data: Array<{
summary: CompileTypeSummary,
metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata|
CompileTypeMetadata|null,
isLibrary: boolean
}>();
}> = [];

constructor(private outputCtx: OutputContext, private symbolResolver: StaticSymbolResolver) {}
constructor(
private outputCtx: OutputContext, private symbolResolver: StaticSymbolResolver,
private summaryResolver: SummaryResolver<StaticSymbol>) {}

addSourceType(
summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|
CompilePipeMetadata|CompileTypeMetadata) {
this.data.set(summary.type.reference, {summary, metadata, isLibrary: false});
this.data.push({summary, metadata, isLibrary: false});
}

addLibType(summary: CompileTypeSummary) {
this.data.set(summary.type.reference, {summary, metadata: null, isLibrary: true});
this.data.push({summary, metadata: null, isLibrary: true});
}

serialize(exportAs: {symbol: StaticSymbol, exportAs: string}[]): void {
serialize(exportAsArr: {symbol: StaticSymbol, exportAs: string}[]): void {
const exportAsBySymbol = new Map<StaticSymbol, string>();
for (const {symbol, exportAs} of exportAsArr) {
exportAsBySymbol.set(symbol, exportAs);
}
const ngModuleSymbols = new Set<StaticSymbol>();

Array.from(this.data.values()).forEach(({summary, metadata, isLibrary}) => {
for (const {summary, metadata, isLibrary} of this.data) {
if (summary.summaryKind === CompileSummaryKind.NgModule) {
// collect the symbols that refer to NgModule classes.
// Note: we can't just rely on `summary.type.summaryKind` to determine this as
// we don't add the summaries of all referenced symbols when we serialize type summaries.
// See serializeSummaries for details.
ngModuleSymbols.add(summary.type.reference);
const modSummary = <CompileNgModuleSummary>summary;
modSummary.modules.forEach((mod) => { ngModuleSymbols.add(mod.reference); });
for (const mod of modSummary.modules) {
ngModuleSymbols.add(mod.reference);
}
}
if (!isLibrary) {
const fnName = summaryForJitName(summary.type.reference.name);
createSummaryForJitFunction(
this.outputCtx, summary.type.reference,
this.serializeSummaryWithDeps(summary, metadata !));
}
});
}

exportAs.forEach((entry) => {
const symbol = entry.symbol;
if (ngModuleSymbols.has(symbol)) {
const jitExportAsName = summaryForJitName(entry.exportAs);
this.outputCtx.statements.push(
o.variable(jitExportAsName).set(this.serializeSummaryRef(symbol)).toDeclStmt(null, [
o.StmtModifier.Exported
]));
ngModuleSymbols.forEach((ngModuleSymbol) => {
if (this.summaryResolver.isLibraryFile(ngModuleSymbol.filePath)) {
let exportAs = exportAsBySymbol.get(ngModuleSymbol) || ngModuleSymbol.name;
const jitExportAsName = summaryForJitName(exportAs);
this.outputCtx.statements.push(o.variable(jitExportAsName)
.set(this.serializeSummaryRef(ngModuleSymbol))
.toDeclStmt(null, [o.StmtModifier.Exported]));
}
});
}
Expand Down Expand Up @@ -378,22 +414,26 @@ class FromJsonDeserializer extends ValueTransformer {
deserialize(libraryFileName: string, json: string): {
moduleName: string | null,
summaries: Summary<StaticSymbol>[],
importAs: {symbol: StaticSymbol, importAs: string}[]
importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[]
} {
const data: {moduleName: string | null, summaries: any[], symbols: any[]} = JSON.parse(json);
const importAs: {symbol: StaticSymbol, importAs: string}[] = [];
this.symbols = [];
data.symbols.forEach((serializedSymbol) => {
const symbol = this.symbolCache.get(
this.summaryResolver.fromSummaryFileName(serializedSymbol.filePath, libraryFileName),
serializedSymbol.name);
this.symbols.push(symbol);
if (serializedSymbol.importAs) {
importAs.push({symbol: symbol, importAs: serializedSymbol.importAs});
const allImportAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] = [];
this.symbols = data.symbols.map(
(serializedSymbol) => this.symbolCache.get(
this.summaryResolver.fromSummaryFileName(serializedSymbol.filePath, libraryFileName),
serializedSymbol.name));
data.symbols.forEach((serializedSymbol, index) => {
const symbol = this.symbols[index];
const importAs = serializedSymbol.importAs;
if (typeof importAs === 'number') {
allImportAs.push({symbol, importAs: this.symbols[importAs]});
} else if (typeof importAs === 'string') {
allImportAs.push(
{symbol, importAs: this.symbolCache.get(ngfactoryFilePath(libraryFileName), importAs)});
}
});
const summaries = visitValue(data.summaries, this, null);
return {moduleName: data.moduleName, summaries, importAs};
const summaries = visitValue(data.summaries, this, null) as Summary<StaticSymbol>[];
return {moduleName: data.moduleName, summaries, importAs: allImportAs};
}

visitStringMap(map: {[key: string]: any}, context: any): any {
Expand All @@ -407,3 +447,16 @@ class FromJsonDeserializer extends ValueTransformer {
}
}
}

function isCall(metadata: any): boolean {
return metadata && metadata.__symbolic === 'call';
}

function isFunctionCall(metadata: any): boolean {
return isCall(metadata) && metadata.expression instanceof StaticSymbol;
}

function isMethodCallOnVariable(metadata: any): boolean {
return isCall(metadata) && metadata.expression && metadata.expression.__symbolic === 'select' &&
metadata.expression.expression instanceof StaticSymbol;
}
12 changes: 11 additions & 1 deletion packages/compiler/src/aot/util.ts
Expand Up @@ -58,4 +58,14 @@ export function summaryForJitName(symbolName: string): string {

export function stripSummaryForJitNameSuffix(symbolName: string): string {
return symbolName.replace(JIT_SUMMARY_NAME, '');
}
}

const LOWERED_SYMBOL = /\u0275\d+/;

export function isLoweredSymbol(name: string) {
return LOWERED_SYMBOL.test(name);
}

export function createLoweredSymbol(id: number): string {
return `\u0275${id}`;
}
1 change: 1 addition & 0 deletions packages/compiler/src/compiler.ts
Expand Up @@ -39,6 +39,7 @@ export * from './aot/static_reflector';
export * from './aot/static_symbol';
export * from './aot/static_symbol_resolver';
export * from './aot/summary_resolver';
export {isLoweredSymbol, createLoweredSymbol} from './aot/util';
export {LazyRoute} from './aot/lazy_routes';
export * from './ast_path';
export * from './summary_resolver';
Expand Down