Skip to content

Commit

Permalink
Scope name lookups in the JSTypeRegistry.
Browse files Browse the repository at this point in the history
This addresses a variety of issues around declaring types in local scopes.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=193740597
  • Loading branch information
concavelenz authored and brad4d committed Apr 23, 2018
1 parent d02b3e1 commit d8b84be
Show file tree
Hide file tree
Showing 14 changed files with 434 additions and 183 deletions.
19 changes: 13 additions & 6 deletions src/com/google/javascript/jscomp/FunctionTypeBuilder.java
Expand Up @@ -799,7 +799,9 @@ private FunctionType getOrCreateConstructor() {
returnType,
classTemplateTypeNames,
isAbstract);
JSType existingType = typeRegistry.getType(fnName);
// We use "getTypeForScope" to specifically check if this was defined for getScopeDeclaredIn()
// so we don't pick up types that are going to be shadowed.
JSType existingType = typeRegistry.getTypeForScope(getScopeDeclaredIn(), fnName);

if (makesStructs) {
fnType.setStruct();
Expand Down Expand Up @@ -833,16 +835,20 @@ private FunctionType getOrCreateConstructor() {

maybeSetBaseType(fnType);

if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) {
typeRegistry.declareType(fnName, fnType.getInstanceType());
// TODO(johnlenz): determine what we are supposed to do for:
// @constructor
// this.Foo = ...
//
if (!fnName.isEmpty() && !fnName.startsWith("this.")) {
typeRegistry.declareTypeForExactScope(getScopeDeclaredIn(), fnName, fnType.getInstanceType());
}
return fnType;
}

private FunctionType getOrCreateInterface() {
FunctionType fnType = null;

JSType type = typeRegistry.getType(fnName);
JSType type = typeRegistry.getType(getScopeDeclaredIn(), fnName);
if (type != null && type.isInstanceType()) {
FunctionType ctor = type.toMaybeObjectType().getConstructor();
if (ctor.isInterface()) {
Expand All @@ -854,8 +860,9 @@ private FunctionType getOrCreateInterface() {
if (fnType == null) {
fnType = typeRegistry.createInterfaceType(
fnName, contents.getSourceNode(), classTemplateTypeNames, makesStructs);
if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) {
typeRegistry.declareType(fnName, fnType.getInstanceType());
if (!fnName.isEmpty()) {
typeRegistry.declareTypeForExactScope(
getScopeDeclaredIn(), fnName, fnType.getInstanceType());
}
maybeSetBaseType(fnType);
}
Expand Down
6 changes: 0 additions & 6 deletions src/com/google/javascript/jscomp/GlobalTypeInfo.java
Expand Up @@ -228,12 +228,6 @@ public JSType getGlobalType(String typeName) {
return getType(null, typeName);
}

@Override
public JSType getType(String typeName) {
return getType(null, typeName);
}

@SuppressWarnings("unchecked")
@Override
public JSType getType(StaticScope scope, String typeName) {
// Primitives are not present in the global scope, so hardcode them
Expand Down
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/LinkedFlowScope.java
Expand Up @@ -309,7 +309,7 @@ private boolean optimizesToSameScope(LinkedFlowScope that) {

@Override
public TypedScope getDeclarationScope() {
return this.syntacticScope;
return syntacticScope;
}

/** Join the two FlowScopes. */
Expand Down
17 changes: 13 additions & 4 deletions src/com/google/javascript/jscomp/TypeInference.java
Expand Up @@ -595,7 +595,15 @@ private FlowScope traverseAssign(Node n, FlowScope scope) {
return scope;
}

private boolean isPossibleMixinApplication(Node lvalue, Node rvalue) {
private static boolean isInExternFile(Node n) {
return NodeUtil.getSourceFile(n).isExtern();
}

private static boolean isPossibleMixinApplication(Node lvalue, Node rvalue) {
if (isInExternFile(lvalue)) {
return true;
}

JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(lvalue);
return jsdoc != null
&& jsdoc.isConstructor()
Expand All @@ -609,8 +617,8 @@ private boolean isPossibleMixinApplication(Node lvalue, Node rvalue) {
* The constructor implements at least one interface. If the constructor is missing some
* properties of the inherited interfaces, this method declares these properties.
*/
private void addMissingInterfaceProperties(JSType constructor) {
if (constructor.isConstructor()) {
private static void addMissingInterfaceProperties(JSType constructor) {
if (constructor != null && constructor.isConstructor()) {
FunctionType f = constructor.toMaybeFunctionType();
ObjectType proto = f.getPrototype();
for (ObjectType interf : f.getImplementedInterfaces()) {
Expand Down Expand Up @@ -1655,7 +1663,8 @@ private JSType getPropertyType(JSType objType, String propName,
if ((propertyType == null || propertyType.isUnknownType())
&& qualifiedName != null) {
// If we find this node in the registry, then we can infer its type.
ObjectType regType = ObjectType.cast(registry.getType(qualifiedName));
ObjectType regType = ObjectType.cast(
registry.getType(scope.getDeclarationScope(), qualifiedName));
if (regType != null) {
propertyType = regType.getConstructor();
}
Expand Down
62 changes: 27 additions & 35 deletions src/com/google/javascript/jscomp/TypedScopeCreator.java
Expand Up @@ -373,8 +373,8 @@ void patchGlobalScope(TypedScope globalScope, Node scriptRoot) {
String typeName = var.getName();
globalScope.undeclare(var);
globalScope.getTypeOfThis().toObjectType().removeProperty(typeName);
if (typeRegistry.getType(typeName) != null) {
typeRegistry.removeType(typeName);
if (typeRegistry.getType(globalScope, typeName) != null) {
typeRegistry.removeType(globalScope, typeName);
}
}

Expand Down Expand Up @@ -664,8 +664,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
case CONST:
defineVar(n);
// Handle typedefs.
if ((n.isVar() ? currentHoistScope : currentScope).isGlobal() && n.hasOneChild()) {
// TODO(bradfordcsmith): Defining typedefs in local scopes should be possible.
if (n.hasOneChild()) {
checkForTypedef(n.getFirstChild(), n.getJSDocInfo());
}
break;
Expand Down Expand Up @@ -769,8 +768,7 @@ private void defineObjectLiteral(Node objectLit) {
String lValueName = NodeUtil.getBestLValueName(lValue);
boolean createdEnumType = false;
if (info != null && info.hasEnumParameterType()) {
boolean global = isLValueRootedInGlobalScope(lValue);
type = createEnumTypeFromNodes(objectLit, lValueName, info, global);
type = createEnumTypeFromNodes(objectLit, lValueName, info);
createdEnumType = true;
}

Expand Down Expand Up @@ -918,8 +916,7 @@ void defineFunctionLiteral(Node n) {
Node lValue = NodeUtil.getBestLValue(n);
JSDocInfo info = NodeUtil.getBestJSDocInfo(n);
String functionName = NodeUtil.getBestLValueName(lValue);
FunctionType functionType =
createFunctionTypeFromNodes(n, functionName, info, lValue);
FunctionType functionType = createFunctionTypeFromNodes(n, functionName, info, lValue);

// Assigning the function type to the function node
setDeferredType(n, functionType);
Expand Down Expand Up @@ -967,6 +964,7 @@ private boolean shouldUseFunctionLiteralType(
if (lValue != null && NodeUtil.isObjectLitKey(lValue)) {
return false;
}
// TODO(johnlenz): consider unifying global and local behavior
return isLValueRootedInGlobalScope(lValue) || !type.isReturnTypeInferred();
}

Expand Down Expand Up @@ -1000,9 +998,7 @@ private FunctionType createFunctionTypeFromNodes(
FunctionType functionType = null;
if (rValue != null
&& rValue.isQualifiedName()
&& lvalueNode != null
// TODO(sdh): Try deleting this global scope check and see what happens.
&& isLValueRootedInGlobalScope(lvalueNode)) {
&& lvalueNode != null) {
TypedVar var = currentScope.getVar(rValue.getQualifiedName());
if (var != null && var.getType() != null && var.getType().isFunctionType()) {
FunctionType aliasedType = var.getType().toMaybeFunctionType();
Expand All @@ -1012,7 +1008,7 @@ && isLValueRootedInGlobalScope(lvalueNode)) {

// TODO(nick): Remove this. This should already be handled by normal type resolution.
if (name != null) {
typeRegistry.declareType(name, functionType.getInstanceType());
typeRegistry.declareType(currentScope, name, functionType.getInstanceType());
}
}
}
Expand Down Expand Up @@ -1187,10 +1183,8 @@ private FunctionType findOverriddenFunction(
* @param rValue The node of the enum.
* @param name The enum's name
* @param info The {@link JSDocInfo} attached to the enum definition.
* @param global Whether to declare the type in the global type registry.
*/
private EnumType createEnumTypeFromNodes(
Node rValue, String name, JSDocInfo info, boolean global) {
private EnumType createEnumTypeFromNodes(Node rValue, String name, JSDocInfo info) {
checkNotNull(info);
checkState(info.hasEnumParameterType());

Expand Down Expand Up @@ -1219,8 +1213,8 @@ private EnumType createEnumTypeFromNodes(
}
}

if (name != null && global) {
typeRegistry.declareType(name, enumType.getElementsType());
if (name != null) {
typeRegistry.declareType(currentScope, name, enumType.getElementsType());
}

return enumType;
Expand Down Expand Up @@ -1512,8 +1506,7 @@ && shouldUseFunctionLiteralType(
if (rValue != null && rValue.isObjectLit()) {
return rValue.getJSType();
} else {
return createEnumTypeFromNodes(
rValue, lValue.getQualifiedName(), info, isLValueRootedInGlobalScope(lValue));
return createEnumTypeFromNodes(rValue, lValue.getQualifiedName(), info);
}
} else if (info.isConstructorOrInterface()) {
return createFunctionTypeFromNodes(rValue, lValue.getQualifiedName(), info, lValue);
Expand All @@ -1531,7 +1524,8 @@ && shouldUseFunctionLiteralType(
&& rValueType.isFunctionType()
&& rValueType.toMaybeFunctionType().hasInstanceType()) {
FunctionType functionType = rValueType.toMaybeFunctionType();
typeRegistry.declareType(lValue.getQualifiedName(), functionType.getInstanceType());
typeRegistry.declareType(
currentScope, lValue.getQualifiedName(), functionType.getInstanceType());
}
return rValueType;
}
Expand Down Expand Up @@ -1669,11 +1663,10 @@ private void checkForClassDefiningCalls(Node n) {
}
}

String singletonGetterClassName =
codingConvention.getSingletonGetterClassName(n);
String singletonGetterClassName = codingConvention.getSingletonGetterClassName(n);
if (singletonGetterClassName != null) {
ObjectType objectType = ObjectType.cast(
typeRegistry.getType(singletonGetterClassName));
typeRegistry.getType(currentScope, singletonGetterClassName));
if (objectType != null) {
FunctionType functionType = objectType.getConstructor();

Expand All @@ -1696,7 +1689,7 @@ private void checkForClassDefiningCalls(Node n) {
if (objectLiteralCast != null) {
if (objectLiteralCast.diagnosticType == null) {
ObjectType type = ObjectType.cast(
typeRegistry.getType(objectLiteralCast.typeName));
typeRegistry.getType(currentScope, objectLiteralCast.typeName));
if (type != null && type.getConstructor() != null) {
setDeferredType(objectLiteralCast.objectNode, type);
objectLiteralCast.objectNode.putBooleanProp(
Expand All @@ -1716,12 +1709,14 @@ private void checkForClassDefiningCalls(Node n) {
private void applyDelegateRelationship(
DelegateRelationship delegateRelationship) {
ObjectType delegatorObject = ObjectType.cast(
typeRegistry.getType(delegateRelationship.delegator));
typeRegistry.getType(currentScope, delegateRelationship.delegator));
ObjectType delegateBaseObject = ObjectType.cast(
typeRegistry.getType(delegateRelationship.delegateBase));
typeRegistry.getType(currentScope, delegateRelationship.delegateBase));
ObjectType delegateSuperObject = ObjectType.cast(
typeRegistry.getType(codingConvention.getDelegateSuperclassName()));
if (delegatorObject != null && delegateBaseObject != null && delegateSuperObject != null) {
typeRegistry.getType(currentScope, codingConvention.getDelegateSuperclassName()));
if (delegatorObject != null
&& delegateBaseObject != null
&& delegateSuperObject != null) {
FunctionType delegatorCtor = delegatorObject.getConstructor();
FunctionType delegateBaseCtor = delegateBaseObject.getConstructor();
FunctionType delegateSuperCtor = delegateSuperObject.getConstructor();
Expand Down Expand Up @@ -1768,9 +1763,7 @@ private void applyDelegateRelationship(
*/
void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info,
Node n, Node parent, Node rhsValue) {
if (currentHoistScope.isGlobal()) {
checkForTypedef(n, info);
}
checkForTypedef(n, info);

Node ownerNode = n.getFirstChild();
String ownerName = ownerNode.getQualifiedName();
Expand Down Expand Up @@ -2055,17 +2048,16 @@ private void checkForTypedef(Node candidate, JSDocInfo info) {
// TODO(nicksantos|user): This is a terrible, terrible hack
// to bail out on recursive typedefs. We'll eventually need
// to handle these properly.
typeRegistry.declareType(typedef, unknownType);
typeRegistry.declareType(currentScope, typedef, unknownType);

JSType realType = info.getTypedefType().evaluate(currentScope, typeRegistry);
if (realType == null) {
report(JSError.make(candidate, MALFORMED_TYPEDEF, typedef));
}

typeRegistry.overwriteDeclaredType(typedef, realType);
typeRegistry.overwriteDeclaredType(currentScope, typedef, realType);
if (candidate.isGetProp()) {
defineSlot(candidate, candidate.getParent(),
getNativeType(NO_TYPE), false);
defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false);
}
}
}
Expand Down
14 changes: 0 additions & 14 deletions src/com/google/javascript/rhino/TypeIRegistry.java
Expand Up @@ -96,20 +96,6 @@ public interface TypeIRegistry extends Serializable {
*/
TypeI getType(StaticScope scope, String typeName);

/**
* Returns the type represented by typeName or null if not found.
*
* If you pass Foo to this method, and Foo can be an instance or a constructor,
* you get the Foo instance, in contrast to TypeIEnv#getNamespaceType,
* where you'd get the Foo constructor.
*
* If Foo is not a nominal type, returns the namespace type.
*
* @deprecated Use #getGlobalType instead.
*/
@Deprecated
TypeI getType(String typeName);

/**
* Returns the type represented by typeName or null if not found.
*
Expand Down

0 comments on commit d8b84be

Please sign in to comment.