Skip to content

Commit

Permalink
[NTI] Handle functions with qualified names that are namespaces.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=97171260
  • Loading branch information
dimvar authored and blickly committed Jun 29, 2015
1 parent 1a48238 commit 788cf9f
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 148 deletions.
5 changes: 2 additions & 3 deletions src/com/google/javascript/jscomp/DiagnosticGroups.java
Expand Up @@ -227,7 +227,6 @@ public DiagnosticGroup forName(String name) {
// JSTypeCreatorFromJSDoc.BAD_JSDOC_ANNOTATION, // JSTypeCreatorFromJSDoc.BAD_JSDOC_ANNOTATION,
JSTypeCreatorFromJSDoc.CONFLICTING_EXTENDED_TYPE, JSTypeCreatorFromJSDoc.CONFLICTING_EXTENDED_TYPE,
JSTypeCreatorFromJSDoc.CONFLICTING_IMPLEMENTED_TYPE, JSTypeCreatorFromJSDoc.CONFLICTING_IMPLEMENTED_TYPE,
// JSTypeCreatorFromJSDoc.CONFLICTING_SHAPE_TYPE,
JSTypeCreatorFromJSDoc.DICT_IMPLEMENTS_INTERF, JSTypeCreatorFromJSDoc.DICT_IMPLEMENTS_INTERF,
JSTypeCreatorFromJSDoc.EXTENDS_NON_OBJECT, JSTypeCreatorFromJSDoc.EXTENDS_NON_OBJECT,
JSTypeCreatorFromJSDoc.EXTENDS_NOT_ON_CTOR_OR_INTERF, JSTypeCreatorFromJSDoc.EXTENDS_NOT_ON_CTOR_OR_INTERF,
Expand All @@ -248,7 +247,7 @@ public DiagnosticGroup forName(String name) {
// GlobalTypeInfo.INVALID_PROP_OVERRIDE, // GlobalTypeInfo.INVALID_PROP_OVERRIDE,
GlobalTypeInfo.LENDS_ON_BAD_TYPE, GlobalTypeInfo.LENDS_ON_BAD_TYPE,
GlobalTypeInfo.MALFORMED_ENUM, GlobalTypeInfo.MALFORMED_ENUM,
// GlobalTypeInfo.MISPLACED_CONST_ANNOTATION, GlobalTypeInfo.MISPLACED_CONST_ANNOTATION,
// GlobalTypeInfo.REDECLARED_PROPERTY, // GlobalTypeInfo.REDECLARED_PROPERTY,
GlobalTypeInfo.STRUCTDICT_WITHOUT_CTOR, GlobalTypeInfo.STRUCTDICT_WITHOUT_CTOR,
GlobalTypeInfo.UNDECLARED_NAMESPACE, GlobalTypeInfo.UNDECLARED_NAMESPACE,
Expand All @@ -261,7 +260,7 @@ public DiagnosticGroup forName(String name) {
TypeValidator.INTERFACE_METHOD_NOT_IMPLEMENTED, TypeValidator.INTERFACE_METHOD_NOT_IMPLEMENTED,
NewTypeInference.ASSERT_FALSE, NewTypeInference.ASSERT_FALSE,
NewTypeInference.CANNOT_BIND_CTOR, NewTypeInference.CANNOT_BIND_CTOR,
// NewTypeInference.CONST_REASSIGNED, NewTypeInference.CONST_REASSIGNED,
NewTypeInference.CROSS_SCOPE_GOTCHA, NewTypeInference.CROSS_SCOPE_GOTCHA,
// NewTypeInference.FAILED_TO_UNIFY, // NewTypeInference.FAILED_TO_UNIFY,
// NewTypeInference.FORIN_EXPECTS_OBJECT, // NewTypeInference.FORIN_EXPECTS_OBJECT,
Expand Down
140 changes: 72 additions & 68 deletions src/com/google/javascript/jscomp/GlobalTypeInfo.java
Expand Up @@ -593,6 +593,13 @@ private void processQualifiedDefinition(Node qnameNode) {
visitEnum(qnameNode); visitEnum(qnameNode);
} else if (NodeUtil.isAliasedNominalTypeDecl(qnameNode)) { } else if (NodeUtil.isAliasedNominalTypeDecl(qnameNode)) {
maybeRecordAliasedNominalType(qnameNode); maybeRecordAliasedNominalType(qnameNode);
} else if (isQualifiedFunctionDefinition(qnameNode)) {
Namespace ns = currentScope.getNamespace(QualifiedName.fromNode(recv));
Scope s = currentScope.getScope(getFunInternalName(qnameNode.getParent().getLastChild()));
QualifiedName pname = new QualifiedName(qnameNode.getLastChild().getString());
if (!ns.isDefined(pname)) {
ns.addScope(pname, s);
}
} else if (!currentScope.isDefined(qnameNode)) { } else if (!currentScope.isDefined(qnameNode)) {
Namespace ns = currentScope.getNamespace(QualifiedName.fromNode(recv)); Namespace ns = currentScope.getNamespace(QualifiedName.fromNode(recv));
String pname = qnameNode.getLastChild().getString(); String pname = qnameNode.getLastChild().getString();
Expand Down Expand Up @@ -658,40 +665,56 @@ public void visit(NodeTraversal t, Node n, Node parent) {
} }
} }


private boolean isQualifiedFunctionDefinition(Node qnameNode) {
Preconditions.checkArgument(qnameNode.isGetProp());
Preconditions.checkArgument(qnameNode.isQualifiedName());
Node parent = qnameNode.getParent();
return parent.isAssign()
&& parent.getParent().isExprResult()
&& parent.getLastChild().isFunction();
}

// Returns true iff it creates a new function namespace // Returns true iff it creates a new function namespace
private boolean mayCreateFunctionNamespace(Node qnameNode) { private boolean mayCreateFunctionNamespace(Node qnameNode) {
// TODO(dimvar): handle functions-as-namespaces declared on namespaces if (!qnameNode.isQualifiedName()) {
if (!qnameNode.isName()) {
return false; return false;
} }
Preconditions.checkState(!currentScope.isNamespace(qnameNode)); QualifiedName qname = QualifiedName.fromNode(qnameNode);
if (currentScope.isKnownFunction(qnameNode.getString())) { Preconditions.checkState(!currentScope.isNamespace(qname));
visitNamespaceHelper(qnameNode, false); if (!currentScope.isKnownFunction(qname)) {
return true; return false;
} }
return false; if (qnameNode.isGetProp()) {
markAssignNodeAsAnalyzed(qnameNode.getParent().getParent());
}
Scope s;
if (qnameNode.isName()) {
// s is the scope that contains the function
s = currentScope.getScope(qnameNode.getString()).getParent();
} else {
s = currentScope;
}
s.addNamespace(qnameNode, qnameNode.isFromExterns());
return true;
} }


private void visitObjlitNamespace(Node qnameNode) { private void visitObjlitNamespace(Node qnameNode) {
visitNamespaceHelper(qnameNode, true); if (currentScope.isDefined(qnameNode)) {
}

private void visitNamespaceHelper(Node qnameNode, boolean isObjlitNamespace) {
if (isObjlitNamespace && currentScope.isDefined(qnameNode)) {
return; return;
} }
if (qnameNode.isGetProp()) { if (qnameNode.isGetProp()) {
Preconditions.checkState(qnameNode.getParent().isAssign()); markAssignNodeAsAnalyzed(qnameNode.getParent());
qnameNode.getParent().putBooleanProp(Node.ANALYZED_DURING_GTI, true);
} }
Scope s; currentScope.addNamespace(qnameNode, qnameNode.isFromExterns());
if (isObjlitNamespace) { }
s = currentScope;
private void markAssignNodeAsAnalyzed(Node maybeAssign) {
if (maybeAssign.isAssign()) {
maybeAssign.putBooleanProp(Node.ANALYZED_DURING_GTI, true);
} else { } else {
// s is the scope that contains the function // No initializer for the property
s = currentScope.getScope(qnameNode.getString()).getParent(); Preconditions.checkState(maybeAssign.isExprResult());
} }
s.addNamespace(qnameNode, qnameNode.isFromExterns());
} }


private void visitTypedef(Node qnameNode) { private void visitTypedef(Node qnameNode) {
Expand Down Expand Up @@ -1046,16 +1069,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
Node grandparent = parent.getParent(); Node grandparent = parent.getParent();
if (grandparent == null || if (grandparent == null ||
!isPrototypePropertyDeclaration(grandparent)) { !isPrototypePropertyDeclaration(grandparent)) {
Scope s = visitFunctionLate(n, null); visitFunctionLate(n, null);
Node name = NodeUtil.getFunctionNameNode(n);
if (name != null && name.isGetProp() && name.isQualifiedName()) {
QualifiedName recv = QualifiedName.fromNode(name.getFirstChild());
String pname = name.getLastChild().getString();
if (currentScope.isNamespace(recv)) {
Namespace ns = currentScope.getNamespace(recv);
ns.addScope(new QualifiedName(pname), s);
}
}
} }
break; break;


Expand Down Expand Up @@ -1203,7 +1217,7 @@ private boolean isStaticCtorProp(Node getProp, Scope s) {
QualifiedName.fromNode(receiverObj)); QualifiedName.fromNode(receiverObj));
} }


/** Returns the newly created scope for this function */ /** Compute the declared type for a given scope. */
private Scope visitFunctionLate(Node fn, RawNominalType ownerType) { private Scope visitFunctionLate(Node fn, RawNominalType ownerType) {
Preconditions.checkArgument(fn.isFunction()); Preconditions.checkArgument(fn.isFunction());
// String fnName = NodeUtil.getFunctionName(fn); // String fnName = NodeUtil.getFunctionName(fn);
Expand All @@ -1212,7 +1226,10 @@ private Scope visitFunctionLate(Node fn, RawNominalType ownerType) {
// } // }
String internalName = getFunInternalName(fn); String internalName = getFunInternalName(fn);
Scope fnScope = currentScope.getScope(internalName); Scope fnScope = currentScope.getScope(internalName);
updateFnScope(fnScope, ownerType); JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(fn);
DeclaredFunctionType declFunType = computeFnDeclaredType(
jsdoc, internalName, fn, ownerType, currentScope);
fnScope.setDeclaredType(declFunType);
return fnScope; return fnScope;
} }


Expand Down Expand Up @@ -1326,8 +1343,7 @@ private void visitNamespacePropertyDeclaration(Node getProp) {


private void visitNamespacePropertyDeclaration( private void visitNamespacePropertyDeclaration(
Node declNode, Node recv, String pname) { Node declNode, Node recv, String pname) {
Preconditions.checkArgument( Preconditions.checkArgument(declNode.isGetProp() || declNode.isStringKey());
declNode.isGetProp() || declNode.isStringKey());
Preconditions.checkArgument(currentScope.isNamespace(recv)); Preconditions.checkArgument(currentScope.isNamespace(recv));
EnumType et = currentScope.getEnum(QualifiedName.fromNode(recv)); EnumType et = currentScope.getEnum(QualifiedName.fromNode(recv));
// If there is a reassignment to one of the enum's members, don't consider // If there is a reassignment to one of the enum's members, don't consider
Expand Down Expand Up @@ -1688,8 +1704,7 @@ private DeclaredFunctionType computeFnDeclaredType(
DeclaredFunctionType result = typeParser.getFunctionType( DeclaredFunctionType result = typeParser.getFunctionType(
fnDoc, functionName, declNode, ctorType, ownerType, parentScope); fnDoc, functionName, declNode, ctorType, ownerType, parentScope);
if (ctorType != null) { if (ctorType != null) {
ctorType.setCtorFunction( ctorType.setCtorFunction(result.toFunctionType(), commonTypes);
result.toFunctionType(), commonTypes.getFunctionType());
} }
return result; return result;
} }
Expand Down Expand Up @@ -1764,19 +1779,6 @@ private DeclaredFunctionType getDeclaredFunctionTypeFromContext(
return null; return null;
} }


/**
* Compute the declared type for a given scope.
*/
private void updateFnScope(Scope fnScope, RawNominalType ownerType) {
Node fn = fnScope.getRoot();
Preconditions.checkState(fn.isFunction());
JSDocInfo fnDoc = NodeUtil.getBestJSDocInfo(fn);
String functionName = getFunInternalName(fn);
DeclaredFunctionType declFunType = computeFnDeclaredType(
fnDoc, functionName, fn, ownerType, currentScope);
fnScope.setDeclaredType(declFunType);
}

private JSType getVarTypeFromAnnotation(Node nameNode) { private JSType getVarTypeFromAnnotation(Node nameNode) {
Preconditions.checkArgument(nameNode.getParent().isVar()); Preconditions.checkArgument(nameNode.getParent().isVar());
Node varNode = nameNode.getParent(); Node varNode = nameNode.getParent();
Expand Down Expand Up @@ -1821,8 +1823,7 @@ private void mayAddPropToPrototype(
if (jsdoc != null && jsdoc.containsFunctionDeclaration()) { if (jsdoc != null && jsdoc.containsFunctionDeclaration()) {
// We're parsing a function declaration without a function initializer // We're parsing a function declaration without a function initializer
methodScope = null; methodScope = null;
methodType = computeFnDeclaredType( methodType = computeFnDeclaredType(jsdoc, pname, defSite, rawType, currentScope);
jsdoc, pname, defSite, rawType, currentScope);
propDeclType = commonTypes.fromFunctionType(methodType.toFunctionType()); propDeclType = commonTypes.fromFunctionType(methodType.toFunctionType());
} else if (jsdoc != null && jsdoc.hasType()) { } else if (jsdoc != null && jsdoc.hasType()) {
// We are parsing a non-function prototype property // We are parsing a non-function prototype property
Expand Down Expand Up @@ -2238,9 +2239,8 @@ public JSType getDeclaredTypeOf(String name) {
if (scopeType != null) { if (scopeType != null) {
return getCommonTypes().fromFunctionType(scopeType.toFunctionType()); return getCommonTypes().fromFunctionType(scopeType.toFunctionType());
} }
} else if (decl.getNamespace() != null) {
return decl.getNamespace().toJSType();
} }
Preconditions.checkState(decl.getNamespace() == null);
return null; return null;
} }
if (name.equals(this.name)) { if (name.equals(this.name)) {
Expand Down Expand Up @@ -2272,27 +2272,27 @@ boolean hasUndeclaredFormalsOrOuters() {
return false; return false;
} }


private Scope getScopeHelper(String fnName) { private Scope getScopeHelper(QualifiedName qname) {
Declaration decl; Declaration decl = getDeclaration(qname, false);
if (fnName.contains(".")) {
decl = getDeclaration(QualifiedName.fromQualifiedString(fnName), false);
} else {
decl = getDeclaration(fnName, false);
}
return decl == null ? null : (Scope) decl.getFunctionScope(); return decl == null ? null : (Scope) decl.getFunctionScope();
} }


boolean isKnownFunction(String fnName) { boolean isKnownFunction(String fnName) {
return getScopeHelper(fnName) != null; Preconditions.checkArgument(!fnName.contains("."));
return getScopeHelper(new QualifiedName(fnName)) != null;
}

boolean isKnownFunction(QualifiedName qname) {
return getScopeHelper(qname) != null;
} }


boolean isExternalFunction(String fnName) { boolean isExternalFunction(String fnName) {
Scope s = Preconditions.checkNotNull(getScopeHelper(fnName)); Scope s = getScopeHelper(new QualifiedName(fnName));
return s.root.isFromExterns(); return s.root.isFromExterns();
} }


Scope getScope(String fnName) { Scope getScope(String fnName) {
Scope s = getScopeHelper(fnName); Scope s = getScopeHelper(new QualifiedName(fnName));
Preconditions.checkState(s != null); Preconditions.checkState(s != null);
return s; return s;
} }
Expand Down Expand Up @@ -2458,6 +2458,8 @@ public Declaration getDeclaration(QualifiedName qname, boolean includeTypes) {
if (qname.isIdentifier()) { if (qname.isIdentifier()) {
return getDeclaration(qname.getLeftmostName(), includeTypes); return getDeclaration(qname.getLeftmostName(), includeTypes);
} }
Preconditions.checkState(!this.isFinalized,
"Namespaces are removed from scopes after finalization");
Namespace ns = getNamespace(qname.getLeftmostName()); Namespace ns = getNamespace(qname.getLeftmostName());
if (ns == null) { if (ns == null) {
return null; return null;
Expand Down Expand Up @@ -2534,15 +2536,17 @@ private void removeTmpData() {
declareUnknownType(QualifiedName.fromQualifiedString(name)); declareUnknownType(QualifiedName.fromQualifiedString(name));
} }
unknownTypeNames = ImmutableSet.of(); unknownTypeNames = ImmutableSet.of();
JSTypes commonTypes = getCommonTypes();
// For now, we put types of namespaces directly into the locals. // For now, we put types of namespaces directly into the locals.
// Alternatively, we could move this into NewTypeInference.initEdgeEnvs // Alternatively, we could move this into NewTypeInference.initEdgeEnvs
for (Map.Entry<String, NamespaceLit> entry : localNamespaces.entrySet()) { for (Map.Entry<String, NamespaceLit> entry : localNamespaces.entrySet()) {
String name = entry.getKey(); String name = entry.getKey();
JSType t = entry.getValue().toJSType(); JSType t = entry.getValue().toJSType(commonTypes);
// If it's a function namespace, add the function type to the result
if (localFunDefs.containsKey(name)) { if (localFunDefs.containsKey(name)) {
FunctionType ft = localFunDefs.get(name).getDeclaredFunctionType().toFunctionType(); t = t.withFunction(
JSType funTypeAsJstype = getCommonTypes().fromFunctionType(ft); localFunDefs.get(name).getDeclaredFunctionType().toFunctionType(),
t = funTypeAsJstype.specialize(t); commonTypes.getFunctionType());
} }
if (externs.containsKey(name)) { if (externs.containsKey(name)) {
externs.put(name, t); externs.put(name, t);
Expand All @@ -2551,7 +2555,7 @@ private void removeTmpData() {
} }
} }
for (Map.Entry<String, EnumType> entry : localEnums.entrySet()) { for (Map.Entry<String, EnumType> entry : localEnums.entrySet()) {
locals.put(entry.getKey(), entry.getValue().toJSType()); locals.put(entry.getKey(), entry.getValue().toJSType(commonTypes));
} }
for (String typedefName : localTypedefs.keySet()) { for (String typedefName : localTypedefs.keySet()) {
locals.put(typedefName, JSType.UNDEFINED); locals.put(typedefName, JSType.UNDEFINED);
Expand Down
44 changes: 33 additions & 11 deletions src/com/google/javascript/jscomp/NewTypeInference.java
Expand Up @@ -975,24 +975,46 @@ private void createSummary(Scope fn) {
JSType summary = commonTypes.fromFunctionType(builder.buildFunction()); JSType summary = commonTypes.fromFunctionType(builder.buildFunction());
println("Function summary for ", fn.getReadableName()); println("Function summary for ", fn.getReadableName());
println("\t", summary); println("\t", summary);
Scope enclosingScope = fn.getParent(); summary = mayChangeSummaryForFunctionsWithProperties(fn, summary);
Node fnNameNode = NodeUtil.getFunctionNameNode(fnRoot);
// TODO(dimvar): handle functions as namespaces that are props of namespaces
String fnName = fnNameNode != null && fnNameNode.isName()
? fnNameNode.getString() : null;
if (fnName != null && enclosingScope.isFunctionNamespace(fnName)) {
JSType namespaceType = enclosingScope.getDeclaredTypeOf(fnName);
// Remove the less precise function type from the namespace type
namespaceType = JSType.meet(namespaceType, JSType.TOP_OBJECT);
summary = summary.specialize(namespaceType);
}
summaries.put(fn, summary); summaries.put(fn, summary);
maybeSetTypeI(fnRoot, summary); maybeSetTypeI(fnRoot, summary);
Node fnNameNode = NodeUtil.getFunctionNameNode(fnRoot);
if (fnNameNode != null) { if (fnNameNode != null) {
maybeSetTypeI(fnNameNode, summary); maybeSetTypeI(fnNameNode, summary);
} }
} }


private JSType mayChangeSummaryForFunctionsWithProperties(Scope fnScope, JSType currentSummary) {
Scope enclosingScope = fnScope.getParent();
Node fnNameNode = NodeUtil.getFunctionNameNode(fnScope.getRoot());
JSType summary = currentSummary;
JSType namespaceType = null;
if (fnNameNode == null) {
return currentSummary;
}
if (fnNameNode.isName()) {
String fnName = fnNameNode.getString();
if (enclosingScope.isFunctionNamespace(fnName)) {
namespaceType = enclosingScope.getDeclaredTypeOf(fnName);
}
} else if (fnNameNode.isQualifiedName()) {
QualifiedName qname = QualifiedName.fromNode(fnNameNode);
JSType rootNs = enclosingScope.getDeclaredTypeOf(qname.getLeftmostName());
namespaceType = rootNs == null ? null : rootNs.getProp(qname.getAllButLeftmost());
}
// After GTI, Namespace objects are no longer stored in scopes, so we can't
// tell with certainty if a property-function is a namespace.
// (isFunctionNamespace only works for variable-functions.)
// The isFunctionWithProperties check is a good-enough substitute.
if (namespaceType != null && namespaceType.isFunctionWithProperties()) {
// Replace the less-precise declared function type with the new function summary.
summary = namespaceType.withFunction(
currentSummary.getFunTypeIfSingletonObj(),
commonTypes.getFunctionType());
}
return summary;
}

// TODO(dimvar): To get the adjusted end-of-fwd type for objs, we must be // TODO(dimvar): To get the adjusted end-of-fwd type for objs, we must be
// able to know whether a property was added during the evaluation of the // able to know whether a property was added during the evaluation of the
// function, or was on the object already. // function, or was on the object already.
Expand Down
11 changes: 5 additions & 6 deletions src/com/google/javascript/jscomp/newtypes/EnumType.java
Expand Up @@ -83,10 +83,10 @@ public JSType getPropType() {
} }


@Override @Override
public JSType toJSType() { public JSType toJSType(JSTypes commonTypes) {
Preconditions.checkState(state == State.RESOLVED); Preconditions.checkState(state == State.RESOLVED);
if (enumObjType == null) { if (enumObjType == null) {
enumObjType = computeObjType(); enumObjType = computeObjType(commonTypes);
} }
return enumObjType; return enumObjType;
} }
Expand Down Expand Up @@ -125,16 +125,15 @@ void resolveEnum(JSType t) {
* var X = { ONE: 1, TWO: 2 }; * var X = { ONE: 1, TWO: 2 };
* the properties of the object literal are constant. * the properties of the object literal are constant.
*/ */
private JSType computeObjType() { private JSType computeObjType(JSTypes commonTypes) {
Preconditions.checkState(enumPropType != null); Preconditions.checkState(enumPropType != null);
PersistentMap<String, Property> propMap = otherProps; PersistentMap<String, Property> propMap = otherProps;
for (String s : props) { for (String s : props) {
propMap = propMap.with(s, propMap = propMap.with(s,
Property.makeConstant(null, enumPropType, enumPropType)); Property.makeConstant(null, enumPropType, enumPropType));
} }
return withNamedTypes( ObjectType obj = ObjectType.makeObjectType(null, propMap, null, false, ObjectKind.UNRESTRICTED);
ObjectType.makeObjectType( return withNamedTypes(commonTypes, obj);
null, propMap, null, false, ObjectKind.UNRESTRICTED));
} }


@Override @Override
Expand Down

0 comments on commit 788cf9f

Please sign in to comment.