Performance optimizations

This commit is contained in:
Anders Hejlsberg 2016-07-14 09:21:18 -07:00
parent 6309ada1fa
commit 44339dd55f
2 changed files with 50 additions and 24 deletions

View File

@ -6244,10 +6244,13 @@ namespace ts {
function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
const targetTypes = target.types;
let len = targetTypes.length;
if (contains(targetTypes, source)) {
return Ternary.True;
}
// The null and undefined types are guaranteed to be at the end of the constituent type list. In order
// to produce the best possible errors we first check the nullable types, such that the last type we
// check and report errors from is a non-nullable type if one is present.
let len = targetTypes.length;
while (len >= 2 && targetTypes[len - 1].flags & TypeFlags.Nullable) {
const related = isRelatedTo(source, targetTypes[len - 1], /*reportErrors*/ false);
if (related) {
@ -6280,10 +6283,13 @@ namespace ts {
function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
const sourceTypes = source.types;
let len = sourceTypes.length;
if (contains(sourceTypes, target)) {
return Ternary.True;
}
// The null and undefined types are guaranteed to be at the end of the constituent type list. In order
// to produce the best possible errors we first check the nullable types, such that the last type we
// check and report errors from is a non-nullable type if one is present.
let len = sourceTypes.length;
while (len >= 2 && sourceTypes[len - 1].flags & TypeFlags.Nullable) {
const related = isRelatedTo(sourceTypes[len - 1], target, /*reportErrors*/ false);
if (related) {
@ -6803,9 +6809,11 @@ namespace ts {
// A source signature partially matches a target signature if the target signature has no fewer required
// parameters and no more overall parameters than the source signature (where a signature with a rest
// parameter is always considered to have more overall parameters than one without).
const sourceRestCount = source.hasRestParameter ? 1 : 0;
const targetRestCount = target.hasRestParameter ? 1 : 0;
if (partialMatch && source.minArgumentCount <= target.minArgumentCount && (
source.hasRestParameter && !target.hasRestParameter ||
source.hasRestParameter === target.hasRestParameter && source.parameters.length >= target.parameters.length)) {
sourceRestCount > targetRestCount ||
sourceRestCount === targetRestCount && source.parameters.length >= target.parameters.length)) {
return true;
}
return false;
@ -7259,10 +7267,17 @@ namespace ts {
function inferFromTypes(source: Type, target: Type) {
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
// Source and target are both unions or both intersections. First, find each
// target constituent type that has an identically matching source constituent
// type, and for each such target constituent type infer from the type to itself.
// When inferring from a type to itself we effectively find all type parameter
// Source and target are both unions or both intersections. If source and target
// are the same type, just relate each constituent type to itself.
if (source === target) {
for (const t of (<UnionOrIntersectionType>source).types) {
inferFromTypes(t, t);
}
return;
}
// Find each target constituent type that has an identically matching source
// constituent type, and for each such target constituent type infer from the type to
// itself. When inferring from a type to itself we effectively find all type parameter
// occurrences within that type and infer themselves as their type arguments.
let matchingTypes: Type[];
for (const t of (<UnionOrIntersectionType>target).types) {
@ -7654,12 +7669,20 @@ namespace ts {
if (declaredType !== assignedType && declaredType.flags & TypeFlags.Union) {
const reducedTypes = filter(declaredType.types, t => typeMaybeAssignableTo(assignedType, t));
if (reducedTypes.length) {
return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes);
return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes, /*noSubtypeReduction*/ true);
}
}
return declaredType;
}
function getTypeFactsOfTypes(types: Type[]): TypeFacts {
let result: TypeFacts = TypeFacts.None;
for (const t of types) {
result |= getTypeFacts(t);
}
return result;
}
function getTypeFacts(type: Type): TypeFacts {
const flags = type.flags;
if (flags & TypeFlags.String) {
@ -7709,7 +7732,7 @@ namespace ts {
return constraint ? getTypeFacts(constraint) : TypeFacts.All;
}
if (flags & TypeFlags.UnionOrIntersection) {
return reduceLeft((<UnionOrIntersectionType>type).types, (flags, type) => flags |= getTypeFacts(type), TypeFacts.None);
return getTypeFactsOfTypes((<UnionOrIntersectionType>type).types);
}
return TypeFacts.All;
}
@ -7885,7 +7908,7 @@ namespace ts {
function filterType(type: Type, f: (t: Type) => boolean): Type {
return type.flags & TypeFlags.Union ?
getUnionType(filter((<UnionType>type).types, f)) :
getUnionType(filter((<UnionType>type).types, f), /*noSubtypeReduction*/ true) :
f(type) ? type : neverType;
}
@ -8039,7 +8062,7 @@ namespace ts {
antecedentTypes.push(type);
}
}
return getUnionType(antecedentTypes);
return getUnionType(antecedentTypes, /*noSubtypeReduction*/ true);
}
function getTypeAtFlowLoopLabel(flow: FlowLabel) {
@ -8059,7 +8082,7 @@ namespace ts {
// the non-looping control flow path that leads to the top.
for (let i = flowLoopStart; i < flowLoopCount; i++) {
if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) {
return getUnionType(flowLoopTypes[i]);
return getUnionType(flowLoopTypes[i], /*noSubtypeReduction*/ true);
}
}
// Add the flow loop junction and reference to the in-process stack and analyze
@ -8088,7 +8111,7 @@ namespace ts {
break;
}
}
return cache[key] = getUnionType(antecedentTypes);
return cache[key] = getUnionType(antecedentTypes, /*noSubtypeReduction*/ true);
}
function isMatchingPropertyAccess(expr: Expression) {
@ -8216,13 +8239,13 @@ namespace ts {
}
const clauseTypes = switchTypes.slice(clauseStart, clauseEnd);
const hasDefaultClause = clauseStart === clauseEnd || contains(clauseTypes, neverType);
const discriminantType = getUnionType(clauseTypes);
const discriminantType = getUnionType(clauseTypes, /*noSubtypeReduction*/ true);
const caseType = discriminantType === neverType ? neverType : filterType(type, t => isTypeComparableTo(discriminantType, t));
if (!hasDefaultClause) {
return caseType;
}
const defaultType = filterType(type, t => !(isUnitType(t) && contains(switchTypes, t)));
return caseType === neverType ? defaultType : getUnionType([caseType, defaultType]);
return caseType === neverType ? defaultType : getUnionType([caseType, defaultType], /*noSubtypeReduction*/ true);
}
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
@ -8266,7 +8289,7 @@ namespace ts {
constructSignatures = getSignaturesOfType(rightType, SignatureKind.Construct);
}
if (constructSignatures && constructSignatures.length) {
targetType = getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature))));
targetType = getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature))), /*noSubtypeReduction*/ true);
}
}
@ -8280,7 +8303,7 @@ namespace ts {
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) {
if (!assumeTrue) {
return type.flags & TypeFlags.Union ?
getUnionType(filter((<UnionType>type).types, t => !isTypeSubtypeOf(t, candidate))) :
getUnionType(filter((<UnionType>type).types, t => !isTypeSubtypeOf(t, candidate)), /*noSubtypeReduction*/ true) :
type;
}
// If the current type is a union type, remove all constituents that aren't assignable to
@ -8288,7 +8311,7 @@ namespace ts {
if (type.flags & TypeFlags.Union) {
const assignableConstituents = filter((<UnionType>type).types, t => isTypeAssignableTo(t, candidate));
if (assignableConstituents.length) {
return getUnionType(assignableConstituents);
return getUnionType(assignableConstituents, /*noSubtypeReduction*/ true);
}
}
// If the candidate type is assignable to the target type, narrow to the candidate type.

View File

@ -91,10 +91,10 @@ namespace ts {
return undefined;
}
export function contains<T>(array: T[], value: T, areEqual?: (a: T, b: T) => boolean): boolean {
export function contains<T>(array: T[], value: T): boolean {
if (array) {
for (const v of array) {
if (areEqual ? areEqual(v, value) : v === value) {
if (v === value) {
return true;
}
}
@ -180,10 +180,13 @@ namespace ts {
let result: T[];
if (array) {
result = [];
for (const item of array) {
if (!contains(result, item, areEqual)) {
result.push(item);
loop: for (const item of array) {
for (const res of result) {
if (areEqual ? areEqual(res, item) : res === item) {
continue loop;
}
}
result.push(item);
}
}
return result;