fix(compiler): generate the correct imports for summary type-check

Summaries should be ignored when importing the types used in a
type-check block.
This commit is contained in:
Chuck Jazdzewski 2017-12-11 08:50:46 -08:00 committed by Alex Rickabaugh
parent d213a20dfc
commit d91ff17adc
5 changed files with 64 additions and 39 deletions

View File

@ -12,6 +12,7 @@ import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
import * as ng from '../index'; import * as ng from '../index';
// TEST_TMPDIR is set by bazel.
const tmpdir = process.env.TEST_TMPDIR || os.tmpdir(); const tmpdir = process.env.TEST_TMPDIR || os.tmpdir();
function getNgRootDir() { function getNgRootDir() {
@ -21,7 +22,6 @@ function getNgRootDir() {
} }
export function writeTempFile(name: string, contents: string): string { export function writeTempFile(name: string, contents: string): string {
// TEST_TMPDIR is set by bazel.
const id = (Math.random() * 1000000).toFixed(0); const id = (Math.random() * 1000000).toFixed(0);
const fn = path.join(tmpdir, `tmp.${id}.${name}`); const fn = path.join(tmpdir, `tmp.${id}.${name}`);
fs.writeFileSync(fn, contents); fs.writeFileSync(fn, contents);
@ -29,8 +29,12 @@ export function writeTempFile(name: string, contents: string): string {
} }
export function makeTempDir(): string { export function makeTempDir(): string {
const id = (Math.random() * 1000000).toFixed(0); let dir: string;
const dir = path.join(tmpdir, `tmp.${id}`); while (true) {
const id = (Math.random() * 1000000).toFixed(0);
dir = path.join(tmpdir, `tmp.${id}`);
if (!fs.existsSync(dir)) break;
}
fs.mkdirSync(dir); fs.mkdirSync(dir);
return dir; return dir;
} }

View File

@ -218,15 +218,14 @@ export class AotCompiler {
const externalReferenceVars = new Map<any, string>(); const externalReferenceVars = new Map<any, string>();
externalReferences.forEach((ref, typeIndex) => { externalReferences.forEach((ref, typeIndex) => {
if (this._host.isSourceFile(ref.filePath)) { externalReferenceVars.set(ref, `_decl${ngModuleIndex}_${typeIndex}`);
externalReferenceVars.set(ref, `_decl${ngModuleIndex}_${typeIndex}`);
}
}); });
externalReferenceVars.forEach((varName, reference) => { externalReferenceVars.forEach((varName, reference) => {
outputCtx.statements.push( outputCtx.statements.push(
o.variable(varName) o.variable(varName)
.set(o.NULL_EXPR.cast(o.DYNAMIC_TYPE)) .set(o.NULL_EXPR.cast(o.DYNAMIC_TYPE))
.toDeclStmt(o.expressionType(outputCtx.importExpr(reference)))); .toDeclStmt(o.expressionType(outputCtx.importExpr(
reference, /* typeParams */ null, /* useSummaries */ false))));
}); });
if (emitFlags & StubEmitFlags.TypeCheck) { if (emitFlags & StubEmitFlags.TypeCheck) {
@ -515,35 +514,38 @@ export class AotCompiler {
} }
private _createOutputContext(genFilePath: string): OutputContext { private _createOutputContext(genFilePath: string): OutputContext {
const importExpr = (symbol: StaticSymbol, typeParams: o.Type[] | null = null) => { const importExpr =
if (!(symbol instanceof StaticSymbol)) { (symbol: StaticSymbol, typeParams: o.Type[] | null = null,
throw new Error(`Internal error: unknown identifier ${JSON.stringify(symbol)}`); useSummaries: boolean = true) => {
} if (!(symbol instanceof StaticSymbol)) {
const arity = this._symbolResolver.getTypeArity(symbol) || 0; throw new Error(`Internal error: unknown identifier ${JSON.stringify(symbol)}`);
const {filePath, name, members} = this._symbolResolver.getImportAs(symbol) || symbol; }
const importModule = this._fileNameToModuleName(filePath, genFilePath); const arity = this._symbolResolver.getTypeArity(symbol) || 0;
const {filePath, name, members} =
this._symbolResolver.getImportAs(symbol, useSummaries) || symbol;
const importModule = this._fileNameToModuleName(filePath, genFilePath);
// It should be good enough to compare filePath to genFilePath and if they are equal // It should be good enough to compare filePath to genFilePath and if they are equal
// there is a self reference. However, ngfactory files generate to .ts but their // there is a self reference. However, ngfactory files generate to .ts but their
// symbols have .d.ts so a simple compare is insufficient. They should be canonical // symbols have .d.ts so a simple compare is insufficient. They should be canonical
// and is tracked by #17705. // and is tracked by #17705.
const selfReference = this._fileNameToModuleName(genFilePath, genFilePath); const selfReference = this._fileNameToModuleName(genFilePath, genFilePath);
const moduleName = importModule === selfReference ? null : importModule; const moduleName = importModule === selfReference ? null : importModule;
// If we are in a type expression that refers to a generic type then supply // If we are in a type expression that refers to a generic type then supply
// the required type parameters. If there were not enough type parameters // the required type parameters. If there were not enough type parameters
// supplied, supply any as the type. Outside a type expression the reference // supplied, supply any as the type. Outside a type expression the reference
// should not supply type parameters and be treated as a simple value reference // should not supply type parameters and be treated as a simple value reference
// to the constructor function itself. // to the constructor function itself.
const suppliedTypeParams = typeParams || []; const suppliedTypeParams = typeParams || [];
const missingTypeParamsCount = arity - suppliedTypeParams.length; const missingTypeParamsCount = arity - suppliedTypeParams.length;
const allTypeParams = const allTypeParams =
suppliedTypeParams.concat(new Array(missingTypeParamsCount).fill(o.DYNAMIC_TYPE)); suppliedTypeParams.concat(new Array(missingTypeParamsCount).fill(o.DYNAMIC_TYPE));
return members.reduce( return members.reduce(
(expr, memberName) => expr.prop(memberName), (expr, memberName) => expr.prop(memberName),
<o.Expression>o.importExpr( <o.Expression>o.importExpr(
new o.ExternalReference(moduleName, name, null), allTypeParams)); new o.ExternalReference(moduleName, name, null), allTypeParams));
}; };
return {statements: [], genFilePath, importExpr}; return {statements: [], genFilePath, importExpr};
} }

View File

@ -98,10 +98,10 @@ export class StaticSymbolResolver {
* *
* @param staticSymbol the symbol for which to generate a import symbol * @param staticSymbol the symbol for which to generate a import symbol
*/ */
getImportAs(staticSymbol: StaticSymbol): StaticSymbol|null { getImportAs(staticSymbol: StaticSymbol, useSummaries: boolean = true): StaticSymbol|null {
if (staticSymbol.members.length) { if (staticSymbol.members.length) {
const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name); const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name);
const baseImportAs = this.getImportAs(baseSymbol); const baseImportAs = this.getImportAs(baseSymbol, useSummaries);
return baseImportAs ? return baseImportAs ?
this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) : this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
null; null;
@ -111,14 +111,14 @@ export class StaticSymbolResolver {
const summarizedName = stripSummaryForJitNameSuffix(staticSymbol.name); const summarizedName = stripSummaryForJitNameSuffix(staticSymbol.name);
const baseSymbol = const baseSymbol =
this.getStaticSymbol(summarizedFileName, summarizedName, staticSymbol.members); this.getStaticSymbol(summarizedFileName, summarizedName, staticSymbol.members);
const baseImportAs = this.getImportAs(baseSymbol); const baseImportAs = this.getImportAs(baseSymbol, useSummaries);
return baseImportAs ? return baseImportAs ?
this.getStaticSymbol( this.getStaticSymbol(
summaryForJitFileName(baseImportAs.filePath), summaryForJitName(baseImportAs.name), summaryForJitFileName(baseImportAs.filePath), summaryForJitName(baseImportAs.name),
baseSymbol.members) : baseSymbol.members) :
null; null;
} }
let result = this.summaryResolver.getImportAs(staticSymbol); let result = (useSummaries && this.summaryResolver.getImportAs(staticSymbol)) || null;
if (!result) { if (!result) {
result = this.importAs.get(staticSymbol) !; result = this.importAs.get(staticSymbol) !;
} }

View File

@ -152,7 +152,7 @@ export function utf8Encode(str: string): string {
export interface OutputContext { export interface OutputContext {
genFilePath: string; genFilePath: string;
statements: o.Statement[]; statements: o.Statement[];
importExpr(reference: any, typeParams?: o.Type[]|null): o.Expression; importExpr(reference: any, typeParams?: o.Type[]|null, useSummaries?: boolean): o.Expression;
} }
export function stringify(token: any): string { export function stringify(token: any): string {

View File

@ -196,6 +196,25 @@ describe('StaticSymbolResolver', () => {
.toBe(symbolCache.get('/test3.d.ts', 'b')); .toBe(symbolCache.get('/test3.d.ts', 'b'));
}); });
it('should ignore summaries for inputAs if requested', () => {
init(
{
'/test.ts': `
export {a} from './test2';
`
},
[], [{
symbol: symbolCache.get('/test2.d.ts', 'a'),
importAs: symbolCache.get('/test3.d.ts', 'b')
}]);
symbolResolver.getSymbolsOf('/test.ts');
expect(
symbolResolver.getImportAs(symbolCache.get('/test2.d.ts', 'a'), /* useSummaries */ false))
.toBeUndefined();
});
it('should calculate importAs for symbols with members based on importAs for symbols without', it('should calculate importAs for symbols with members based on importAs for symbols without',
() => { () => {
init( init(