Incremental prototype+prototype assignment work

Had to fix nested incremental prototype detection, so I'll probably
merge this branch back into the PR branch.
This commit is contained in:
Nathan Shively-Sanders
2018-02-22 11:04:29 -08:00
parent b14cf4ef9a
commit 41fba6f34b
6 changed files with 91 additions and 52 deletions

View File

@@ -2367,6 +2367,7 @@ namespace ts {
/** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */
function bindPrototypeAssignment(node: BinaryExpression) {
node.left.parent = node;
node.right.parent = node;
bindPropertyAssignment(node.left as PropertyAccessEntityNameExpression, node.left as PropertyAccessEntityNameExpression, /*isPrototypeProperty*/ false);
}
@@ -2416,7 +2417,7 @@ namespace ts {
function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean) {
let symbol = getJSInitializerSymbol(lookupSymbolForPropertyAccess(name));
const isToplevelNamespaceableInitializer = isBinaryExpression(propertyAccess.parent) ?
propertyAccess.parent.parent.parent.kind === SyntaxKind.SourceFile && (getJavascriptInitializer(propertyAccess.parent.right) || isJavascriptPrototypeAssignment(propertyAccess.parent)) :
propertyAccess.parent.parent.parent.kind === SyntaxKind.SourceFile && getJavascriptInitializer(propertyAccess.parent.right) :
propertyAccess.parent.parent.kind === SyntaxKind.SourceFile;
if (!isPrototypeProperty && (!symbol || !(symbol.flags & SymbolFlags.Namespace)) && isToplevelNamespaceableInitializer) {
// make symbols or add declarations for intermediate containers

View File

@@ -17895,32 +17895,33 @@ namespace ts {
function getJavaScriptClassType(symbol: Symbol): Type | undefined {
if (isDeclarationOfFunctionOrClassExpression(symbol)) {
symbol = getSymbolOfNode((<VariableDeclaration>symbol.valueDeclaration).initializer);
symbol = getSymbolOfNode((symbol.valueDeclaration as VariableDeclaration).initializer);
}
// TODO: Could stick members on this somehow
let assigned = getAssignedClassType(symbol);
let inferred: Type | undefined;
if (isJavaScriptConstructor(symbol.valueDeclaration)) {
return getInferredClassType(symbol);
}
// // OR: Get them another way
const otherSymbol: Symbol = getOtherSymbol(symbol.valueDeclaration);
if (otherSymbol) {
const prototype = forEach(otherSymbol.declarations, d => getAssignedJavascriptPrototype(d.parent));
if (prototype) {
// NOTE: Should be able to check the original symbol for other prototype declarations, right?
// Not sure why not. Maybe intersecting them would work then.
return checkExpression(prototype);
}
inferred = getInferredClassType(symbol);
}
if (symbol.flags & SymbolFlags.Variable) {
const valueType = getTypeOfSymbol(symbol);
if (valueType.symbol && !isInferredClassType(valueType) && isJavaScriptConstructor(valueType.symbol.valueDeclaration)) {
return getInferredClassType(valueType.symbol);
inferred = getInferredClassType(valueType.symbol);
}
}
return !inferred ? assigned :
!assigned ? inferred :
getIntersectionType([inferred, assigned]);
}
function getOtherSymbol(node: Node) {
return node && node.parent && isBinaryExpression(node.parent) && getSymbolOfNode(node.parent.left);
function getAssignedClassType(symbol: Symbol) {
const decl = symbol.valueDeclaration;
const assignmentSymbol = decl && decl.parent && isBinaryExpression(decl.parent) && getSymbolOfNode(decl.parent.left);
if (assignmentSymbol) {
const prototype = forEach(assignmentSymbol.declarations, d => getAssignedJavascriptPrototype(d.parent));
if (prototype) {
return checkExpression(prototype);
}
}
}
function getInferredClassType(symbol: Symbol) {

View File

@@ -1483,12 +1483,6 @@ namespace ts {
return false;
}
export function isJavascriptPrototypeAssignment(e: BinaryExpression) {
return isObjectLiteralExpression(e.right) &&
isPropertyAccessExpression(e.left) &&
e.left.name.escapedText === "prototype";
}
export function getJSInitializerSymbol(symbol: Symbol) {
if (!symbol || !symbol.valueDeclaration) {
return symbol;
@@ -1525,9 +1519,12 @@ namespace ts {
const e = skipParentheses(initializer.expression);
return e.kind === SyntaxKind.FunctionExpression || e.kind === SyntaxKind.ArrowFunction ? initializer : undefined;
}
if (initializer.kind === SyntaxKind.FunctionExpression ||
initializer.kind === SyntaxKind.ClassExpression ||
isObjectLiteralExpression(initializer) && initializer.properties.length === 0) {
if (initializer.kind === SyntaxKind.FunctionExpression || initializer.kind === SyntaxKind.ClassExpression) {
return initializer;
}
if (isObjectLiteralExpression(initializer) &&
(initializer.properties.length === 0 ||
isBinaryExpression(initializer.parent) && isPropertyAccessExpression(initializer.parent.left) && initializer.parent.left.name.escapedText === "prototype")) {
return initializer;
}
}
@@ -1599,32 +1596,38 @@ namespace ts {
// module.exports = expr
return SpecialPropertyAssignmentKind.ModuleExports;
}
// TODO: Can probably unify these checks with those in the second half
else if (lhs.name.escapedText === "prototype") {
return SpecialPropertyAssignmentKind.Prototype;
}
else {
// F.x = expr
return SpecialPropertyAssignmentKind.Property;
}
}
else if (lhs.name.escapedText === "prototype" && expr.right.kind === SyntaxKind.ObjectLiteralExpression) {
// F.prototype = { ... }
return SpecialPropertyAssignmentKind.Prototype;
}
else if (lhs.expression.kind === SyntaxKind.ThisKeyword) {
return SpecialPropertyAssignmentKind.ThisProperty;
}
else if (isPropertyAccessExpression(lhs.expression)) {
// chained dot, e.g. x.y.z = expr; this var is the 'x.y' part
if (isIdentifier(lhs.expression.expression)) {
// module.exports.name = expr
if (lhs.expression.expression.escapedText === "module" && lhs.expression.name.escapedText === "exports") {
else if (isEntityNameExpression(lhs.expression)) {
if (lhs.name.escapedText === "prototype" && isObjectLiteralExpression(expr.right)) {
// F.prototype = { ... }
return SpecialPropertyAssignmentKind.Prototype;
}
else if (isPropertyAccessExpression(lhs.expression)) {
// chained dot, e.g. x.y.z = expr; this var is the 'x.y' part
if (isIdentifier(lhs.expression.expression) &&
lhs.expression.expression.escapedText === "module" &&
lhs.expression.name.escapedText === "exports") {
// module.exports.name = expr
return SpecialPropertyAssignmentKind.ExportsProperty;
}
if (lhs.expression.name.escapedText === "prototype") {
// F.G....prototype.x = expr
return SpecialPropertyAssignmentKind.PrototypeProperty;
}
}
if (isEntityNameExpression(lhs.expression)) {
return SpecialPropertyAssignmentKind.Property;
}
// F.G...x = expr
return SpecialPropertyAssignmentKind.Property;
}
return SpecialPropertyAssignmentKind.None;