Having ngsummaries emit line/char numbers causes white space change to retrigger upstream recompilations. PR Close #22840
		
			
				
	
	
		
			481 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			481 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @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 {CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompileProviderMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary} from '../compile_metadata';
 | 
						|
import * as o from '../output/output_ast';
 | 
						|
import {Summary, SummaryResolver} from '../summary_resolver';
 | 
						|
import {OutputContext, ValueTransformer, ValueVisitor, visitValue} from '../util';
 | 
						|
 | 
						|
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
 | 
						|
import {ResolvedStaticSymbol, StaticSymbolResolver, unwrapResolvedMetadata} from './static_symbol_resolver';
 | 
						|
import {isLoweredSymbol, ngfactoryFilePath, summaryForJitFileName, summaryForJitName} from './util';
 | 
						|
 | 
						|
export function serializeSummaries(
 | 
						|
    srcFileName: string, forJitCtx: OutputContext | null,
 | 
						|
    summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver,
 | 
						|
    symbols: ResolvedStaticSymbol[], types: {
 | 
						|
      summary: CompileTypeSummary,
 | 
						|
      metadata: CompileNgModuleMetadata | CompileDirectiveMetadata | CompilePipeMetadata |
 | 
						|
          CompileTypeMetadata
 | 
						|
    }[]): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
 | 
						|
  const toJsonSerializer = new ToJsonSerializer(symbolResolver, summaryResolver, srcFileName);
 | 
						|
 | 
						|
  // for symbols, we use everything except for the class metadata itself
 | 
						|
  // (we keep the statics though), as the class metadata is contained in the
 | 
						|
  // CompileTypeSummary.
 | 
						|
  symbols.forEach(
 | 
						|
      (resolvedSymbol) => toJsonSerializer.addSummary(
 | 
						|
          {symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata}));
 | 
						|
 | 
						|
  // Add type summaries.
 | 
						|
  types.forEach(({summary, metadata}) => {
 | 
						|
    toJsonSerializer.addSummary(
 | 
						|
        {symbol: summary.type.reference, metadata: undefined, type: summary});
 | 
						|
  });
 | 
						|
  const {json, exportAs} = toJsonSerializer.serialize();
 | 
						|
  if (forJitCtx) {
 | 
						|
    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) {
 | 
						|
        forJitSerializer.addLibType(summary.type);
 | 
						|
      }
 | 
						|
    });
 | 
						|
    forJitSerializer.serialize(exportAs);
 | 
						|
  }
 | 
						|
  return {json, exportAs};
 | 
						|
}
 | 
						|
 | 
						|
export function deserializeSummaries(
 | 
						|
    symbolCache: StaticSymbolCache, summaryResolver: SummaryResolver<StaticSymbol>,
 | 
						|
    libraryFileName: string, json: string): {
 | 
						|
  moduleName: string | null,
 | 
						|
  summaries: Summary<StaticSymbol>[],
 | 
						|
  importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[]
 | 
						|
} {
 | 
						|
  const deserializer = new FromJsonDeserializer(symbolCache, summaryResolver);
 | 
						|
  return deserializer.deserialize(libraryFileName, json);
 | 
						|
}
 | 
						|
 | 
						|
export function createForJitStub(outputCtx: OutputContext, reference: StaticSymbol) {
 | 
						|
  return createSummaryForJitFunction(outputCtx, reference, o.NULL_EXPR);
 | 
						|
}
 | 
						|
 | 
						|
function createSummaryForJitFunction(
 | 
						|
    outputCtx: OutputContext, reference: StaticSymbol, value: o.Expression) {
 | 
						|
  const fnName = summaryForJitName(reference.name);
 | 
						|
  outputCtx.statements.push(
 | 
						|
      o.fn([], [new o.ReturnStatement(value)], new o.ArrayType(o.DYNAMIC_TYPE)).toDeclStmt(fnName, [
 | 
						|
        o.StmtModifier.Final, o.StmtModifier.Exported
 | 
						|
      ]));
 | 
						|
}
 | 
						|
 | 
						|
const enum SerializationFlags {
 | 
						|
  None = 0,
 | 
						|
  ResolveValue = 1,
 | 
						|
}
 | 
						|
 | 
						|
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>();
 | 
						|
  private processedSummaries: any[] = [];
 | 
						|
  private moduleName: string|null;
 | 
						|
 | 
						|
  unprocessedSymbolSummariesBySymbol = new Map<StaticSymbol, Summary<StaticSymbol>>();
 | 
						|
 | 
						|
  constructor(
 | 
						|
      private symbolResolver: StaticSymbolResolver,
 | 
						|
      private summaryResolver: SummaryResolver<StaticSymbol>, private srcFileName: string) {
 | 
						|
    super();
 | 
						|
    this.moduleName = symbolResolver.getKnownModuleName(srcFileName);
 | 
						|
  }
 | 
						|
 | 
						|
  addSummary(summary: Summary<StaticSymbol>) {
 | 
						|
    let unprocessedSummary = this.unprocessedSymbolSummariesBySymbol.get(summary.symbol);
 | 
						|
    let processedSummary = this.processedSummaryBySymbol.get(summary.symbol);
 | 
						|
    if (!unprocessedSummary) {
 | 
						|
      unprocessedSummary = {symbol: summary.symbol, metadata: undefined};
 | 
						|
      this.unprocessedSymbolSummariesBySymbol.set(summary.symbol, unprocessedSummary);
 | 
						|
      processedSummary = {symbol: this.processValue(summary.symbol, SerializationFlags.None)};
 | 
						|
      this.processedSummaries.push(processedSummary);
 | 
						|
      this.processedSummaryBySymbol.set(summary.symbol, processedSummary);
 | 
						|
    }
 | 
						|
    if (!unprocessedSummary.metadata && summary.metadata) {
 | 
						|
      let metadata = summary.metadata || {};
 | 
						|
      if (metadata.__symbolic === 'class') {
 | 
						|
        // For classes, we keep everything except their class decorators.
 | 
						|
        // We need to keep e.g. the ctor args, method names, method decorators
 | 
						|
        // so that the class can be extended in another compilation unit.
 | 
						|
        // We don't keep the class decorators as
 | 
						|
        // 1) they refer to data
 | 
						|
        //   that should not cause a rebuild of downstream compilation units
 | 
						|
        //   (e.g. inline templates of @Component, or @NgModule.declarations)
 | 
						|
        // 2) their data is already captured in TypeSummaries, e.g. DirectiveSummary.
 | 
						|
        const clone: {[key: string]: any} = {};
 | 
						|
        Object.keys(metadata).forEach((propName) => {
 | 
						|
          if (propName !== 'decorators') {
 | 
						|
            clone[propName] = metadata[propName];
 | 
						|
          }
 | 
						|
        });
 | 
						|
        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;
 | 
						|
      // Note: We don't add the summaries of all referenced symbols as for the ResolvedSymbols,
 | 
						|
      // as the type summaries already contain the transitive data that they require
 | 
						|
      // (in a minimal way).
 | 
						|
      processedSummary.type = this.processValue(summary.type, SerializationFlags.None);
 | 
						|
      // except for reexported directives / pipes, so we need to store
 | 
						|
      // their summaries explicitly.
 | 
						|
      if (summary.type.summaryKind === CompileSummaryKind.NgModule) {
 | 
						|
        const ngModuleSummary = <CompileNgModuleSummary>summary.type;
 | 
						|
        ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
 | 
						|
          const symbol: StaticSymbol = id.reference;
 | 
						|
          if (this.summaryResolver.isLibraryFile(symbol.filePath) &&
 | 
						|
              !this.unprocessedSymbolSummariesBySymbol.has(symbol)) {
 | 
						|
            const summary = this.summaryResolver.resolveSummary(symbol);
 | 
						|
            if (summary) {
 | 
						|
              this.addSummary(summary);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  serialize(): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
 | 
						|
    const exportAs: {symbol: StaticSymbol, exportAs: string}[] = [];
 | 
						|
    const json = JSON.stringify({
 | 
						|
      moduleName: this.moduleName,
 | 
						|
      summaries: this.processedSummaries,
 | 
						|
      symbols: this.symbols.map((symbol, index) => {
 | 
						|
        symbol.assertNoMembers();
 | 
						|
        let importAs: string|number = undefined !;
 | 
						|
        if (this.summaryResolver.isLibraryFile(symbol.filePath)) {
 | 
						|
          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 {
 | 
						|
          __symbol: index,
 | 
						|
          name: symbol.name,
 | 
						|
          filePath: this.summaryResolver.toSummaryFileName(symbol.filePath, this.srcFileName),
 | 
						|
          importAs: importAs
 | 
						|
        };
 | 
						|
      })
 | 
						|
    });
 | 
						|
    return {json, exportAs};
 | 
						|
  }
 | 
						|
 | 
						|
  private processValue(value: any, flags: SerializationFlags): any {
 | 
						|
    return visitValue(value, this, flags);
 | 
						|
  }
 | 
						|
 | 
						|
  visitOther(value: any, context: any): any {
 | 
						|
    if (value instanceof StaticSymbol) {
 | 
						|
      let baseSymbol = this.symbolResolver.getStaticSymbol(value.filePath, value.name);
 | 
						|
      const index = this.visitStaticSymbol(baseSymbol, context);
 | 
						|
      return {__symbol: index, members: value.members};
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Strip line and character numbers from ngsummaries.
 | 
						|
   * Emitting them causes white spaces changes to retrigger upstream
 | 
						|
   * recompilations in bazel.
 | 
						|
   * TODO: find out a way to have line and character numbers in errors without
 | 
						|
   * excessive recompilation in bazel.
 | 
						|
   */
 | 
						|
  visitStringMap(map: {[key: string]: any}, context: any): any {
 | 
						|
    if (map['__symbolic'] === 'resolved') {
 | 
						|
      return visitValue(map.symbol, this, context);
 | 
						|
    }
 | 
						|
    if (map['__symbolic'] === 'error') {
 | 
						|
      delete map['line'];
 | 
						|
      delete map['character'];
 | 
						|
    }
 | 
						|
    return super.visitStringMap(map, context);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns null if the options.resolveValue is true, and the summary for the symbol
 | 
						|
   * resolved to a type or could not be resolved.
 | 
						|
   */
 | 
						|
  private visitStaticSymbol(baseSymbol: StaticSymbol, flags: SerializationFlags): number {
 | 
						|
    let index: number|undefined|null = this.indexBySymbol.get(baseSymbol);
 | 
						|
    let summary: Summary<StaticSymbol>|null = null;
 | 
						|
    if (flags & SerializationFlags.ResolveValue &&
 | 
						|
        this.summaryResolver.isLibraryFile(baseSymbol.filePath)) {
 | 
						|
      if (this.unprocessedSymbolSummariesBySymbol.has(baseSymbol)) {
 | 
						|
        // the summary for this symbol was already added
 | 
						|
        // -> nothing to do.
 | 
						|
        return index !;
 | 
						|
      }
 | 
						|
      summary = this.loadSummary(baseSymbol);
 | 
						|
      if (summary && summary.metadata instanceof StaticSymbol) {
 | 
						|
        // The summary is a reexport
 | 
						|
        index = this.visitStaticSymbol(summary.metadata, flags);
 | 
						|
        // reset the summary as it is just a reexport, so we don't want to store it.
 | 
						|
        summary = null;
 | 
						|
      }
 | 
						|
    } else if (index != null) {
 | 
						|
      // Note: == on purpose to compare with undefined!
 | 
						|
      // No summary and the symbol is already added -> nothing to do.
 | 
						|
      return index;
 | 
						|
    }
 | 
						|
    // Note: == on purpose to compare with undefined!
 | 
						|
    if (index == null) {
 | 
						|
      index = this.symbols.length;
 | 
						|
      this.symbols.push(baseSymbol);
 | 
						|
    }
 | 
						|
    this.indexBySymbol.set(baseSymbol, index);
 | 
						|
    if (summary) {
 | 
						|
      this.addSummary(summary);
 | 
						|
    }
 | 
						|
    return index;
 | 
						|
  }
 | 
						|
 | 
						|
  private loadSummary(symbol: StaticSymbol): Summary<StaticSymbol>|null {
 | 
						|
    let summary = this.summaryResolver.resolveSummary(symbol);
 | 
						|
    if (!summary) {
 | 
						|
      // some symbols might originate from a plain typescript library
 | 
						|
      // that just exported .d.ts and .metadata.json files, i.e. where no summary
 | 
						|
      // files were created.
 | 
						|
      const resolvedSymbol = this.symbolResolver.resolveSymbol(symbol);
 | 
						|
      if (resolvedSymbol) {
 | 
						|
        summary = {symbol: resolvedSymbol.symbol, metadata: resolvedSymbol.metadata};
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return summary;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class ForJitSerializer {
 | 
						|
  private data: Array<{
 | 
						|
    summary: CompileTypeSummary,
 | 
						|
    metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata|
 | 
						|
    CompileTypeMetadata|null,
 | 
						|
    isLibrary: boolean
 | 
						|
  }> = [];
 | 
						|
 | 
						|
  constructor(
 | 
						|
      private outputCtx: OutputContext, private symbolResolver: StaticSymbolResolver,
 | 
						|
      private summaryResolver: SummaryResolver<StaticSymbol>) {}
 | 
						|
 | 
						|
  addSourceType(
 | 
						|
      summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|
 | 
						|
      CompilePipeMetadata|CompileTypeMetadata) {
 | 
						|
    this.data.push({summary, metadata, isLibrary: false});
 | 
						|
  }
 | 
						|
 | 
						|
  addLibType(summary: CompileTypeSummary) {
 | 
						|
    this.data.push({summary, metadata: null, isLibrary: true});
 | 
						|
  }
 | 
						|
 | 
						|
  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>();
 | 
						|
 | 
						|
    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;
 | 
						|
        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 !));
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    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]));
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  private serializeSummaryWithDeps(
 | 
						|
      summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|
 | 
						|
      CompilePipeMetadata|CompileTypeMetadata): o.Expression {
 | 
						|
    const expressions: o.Expression[] = [this.serializeSummary(summary)];
 | 
						|
    let providers: CompileProviderMetadata[] = [];
 | 
						|
    if (metadata instanceof CompileNgModuleMetadata) {
 | 
						|
      expressions.push(...
 | 
						|
                       // For directives / pipes, we only add the declared ones,
 | 
						|
                       // and rely on transitively importing NgModules to get the transitive
 | 
						|
                       // summaries.
 | 
						|
                       metadata.declaredDirectives.concat(metadata.declaredPipes)
 | 
						|
                           .map(type => type.reference)
 | 
						|
                           // For modules,
 | 
						|
                           // we also add the summaries for modules
 | 
						|
                           // from libraries.
 | 
						|
                           // This is ok as we produce reexports for all transitive modules.
 | 
						|
                           .concat(metadata.transitiveModule.modules.map(type => type.reference)
 | 
						|
                                       .filter(ref => ref !== metadata.type.reference))
 | 
						|
                           .map((ref) => this.serializeSummaryRef(ref)));
 | 
						|
      // Note: We don't use `NgModuleSummary.providers`, as that one is transitive,
 | 
						|
      // and we already have transitive modules.
 | 
						|
      providers = metadata.providers;
 | 
						|
    } else if (summary.summaryKind === CompileSummaryKind.Directive) {
 | 
						|
      const dirSummary = <CompileDirectiveSummary>summary;
 | 
						|
      providers = dirSummary.providers.concat(dirSummary.viewProviders);
 | 
						|
    }
 | 
						|
    // Note: We can't just refer to the `ngsummary.ts` files for `useClass` providers (as we do for
 | 
						|
    // declaredDirectives / declaredPipes), as we allow
 | 
						|
    // providers without ctor arguments to skip the `@Injectable` decorator,
 | 
						|
    // i.e. we didn't generate .ngsummary.ts files for these.
 | 
						|
    expressions.push(
 | 
						|
        ...providers.filter(provider => !!provider.useClass).map(provider => this.serializeSummary({
 | 
						|
          summaryKind: CompileSummaryKind.Injectable, type: provider.useClass
 | 
						|
        } as CompileTypeSummary)));
 | 
						|
    return o.literalArr(expressions);
 | 
						|
  }
 | 
						|
 | 
						|
  private serializeSummaryRef(typeSymbol: StaticSymbol): o.Expression {
 | 
						|
    const jitImportedSymbol = this.symbolResolver.getStaticSymbol(
 | 
						|
        summaryForJitFileName(typeSymbol.filePath), summaryForJitName(typeSymbol.name));
 | 
						|
    return this.outputCtx.importExpr(jitImportedSymbol);
 | 
						|
  }
 | 
						|
 | 
						|
  private serializeSummary(data: {[key: string]: any}): o.Expression {
 | 
						|
    const outputCtx = this.outputCtx;
 | 
						|
 | 
						|
    class Transformer implements ValueVisitor {
 | 
						|
      visitArray(arr: any[], context: any): any {
 | 
						|
        return o.literalArr(arr.map(entry => visitValue(entry, this, context)));
 | 
						|
      }
 | 
						|
      visitStringMap(map: {[key: string]: any}, context: any): any {
 | 
						|
        return new o.LiteralMapExpr(Object.keys(map).map(
 | 
						|
            (key) => new o.LiteralMapEntry(key, visitValue(map[key], this, context), false)));
 | 
						|
      }
 | 
						|
      visitPrimitive(value: any, context: any): any { return o.literal(value); }
 | 
						|
      visitOther(value: any, context: any): any {
 | 
						|
        if (value instanceof StaticSymbol) {
 | 
						|
          return outputCtx.importExpr(value);
 | 
						|
        } else {
 | 
						|
          throw new Error(`Illegal State: Encountered value ${value}`);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return visitValue(data, new Transformer(), null);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class FromJsonDeserializer extends ValueTransformer {
 | 
						|
  private symbols: StaticSymbol[];
 | 
						|
 | 
						|
  constructor(
 | 
						|
      private symbolCache: StaticSymbolCache,
 | 
						|
      private summaryResolver: SummaryResolver<StaticSymbol>) {
 | 
						|
    super();
 | 
						|
  }
 | 
						|
 | 
						|
  deserialize(libraryFileName: string, json: string): {
 | 
						|
    moduleName: string | null,
 | 
						|
    summaries: Summary<StaticSymbol>[],
 | 
						|
    importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[]
 | 
						|
  } {
 | 
						|
    const data: {moduleName: string | null, summaries: any[], symbols: any[]} = JSON.parse(json);
 | 
						|
    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) as Summary<StaticSymbol>[];
 | 
						|
    return {moduleName: data.moduleName, summaries, importAs: allImportAs};
 | 
						|
  }
 | 
						|
 | 
						|
  visitStringMap(map: {[key: string]: any}, context: any): any {
 | 
						|
    if ('__symbol' in map) {
 | 
						|
      const baseSymbol = this.symbols[map['__symbol']];
 | 
						|
      const members = map['members'];
 | 
						|
      return members.length ? this.symbolCache.get(baseSymbol.filePath, baseSymbol.name, members) :
 | 
						|
                              baseSymbol;
 | 
						|
    } else {
 | 
						|
      return super.visitStringMap(map, context);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function isCall(metadata: any): boolean {
 | 
						|
  return metadata && metadata.__symbolic === 'call';
 | 
						|
}
 | 
						|
 | 
						|
function isFunctionCall(metadata: any): boolean {
 | 
						|
  return isCall(metadata) && unwrapResolvedMetadata(metadata.expression) instanceof StaticSymbol;
 | 
						|
}
 | 
						|
 | 
						|
function isMethodCallOnVariable(metadata: any): boolean {
 | 
						|
  return isCall(metadata) && metadata.expression && metadata.expression.__symbolic === 'select' &&
 | 
						|
      unwrapResolvedMetadata(metadata.expression.expression) instanceof StaticSymbol;
 | 
						|
}
 |