Code with Finding: |
class TightenTypes.CreateScope {
// TODO(user): handle object literals like { a: new Foo };
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.VAR:
// Variable declaration, e.g. var a = b;
Node name;
for (name = n.getFirstChild(); name != null; name = name.getNext()) {
if (inExterns) {
// In externs, we have to trust the type information because there
// are not necessarily assignments to the variables, calls to the
// functions, etc.
scope.declareSlot(name.getString(), n, createType(name, scope));
} else {
scope.declareSlot(name.getString(), n);
if (name.getFirstChild() != null) {
addActions(createAssignmentActions(
name, name.getFirstChild(), n));
}
}
}
break;
case Token.GETPROP:
// Property access, e.g. a.b = c;
if (inExterns) {
ConcreteType type = inferConcreteType(getTopScope(), n);
// We only need to set a type if one hasn't been assigned by
// something else, e.g. an ASSIGN node.
if (type.isNone()) {
ConcreteScope scope =
(ConcreteScope) inferConcreteType(getTopScope(),
n.getFirstChild()).getScope();
if (scope != null) {
type = createType(n.getJSType());
if (type.isNone() || type.isAll()) {
break;
}
type = createUnionWithSubTypes(type);
Node nameNode = n.getLastChild();
scope.declareSlot(nameNode.getString(), n, type);
}
}
}
break;
case Token.FUNCTION:
// Function declaration, e.g. function Foo() {};
if (NodeUtil.isFunctionDeclaration(n)) {
if (!n.getJSType().isNoObjectType()) {
ConcreteFunctionType type = createConcreteFunction(n, scope);
scope.declareSlot(n.getFirstChild().getString(), n, type);
if (inExterns && type.getInstanceType() != null) {
// We must assume all extern types are instantiated since they
// can be created by the browser itself.
allInstantiatedTypes.add(type.getInstanceType());
}
}
}
break;
case Token.ASSIGN:
// Variable assignment, e.g. a = b;
Node lhs = n.getFirstChild();
if (inExterns) {
// Again, we have to trust the externs.
ConcreteScope scope;
if (lhs.getType() == Token.GETPROP) {
ConcreteType type = inferConcreteType(getTopScope(),
lhs.getFirstChild());
scope = (ConcreteScope) type.getScope();
} else {
scope = getTopScope();
}
if (scope == null) break;
ConcreteType type = inferConcreteType(getTopScope(), n);
if (type.isNone() || type.isAll()) {
break;
}
if (type.isFunction()) {
if (lhs.getJSType() == null
|| !(lhs.getJSType() instanceof FunctionType)) {
break;
}
ConcreteType retType = createType(((FunctionType)
lhs.getJSType().restrictByNotNullOrUndefined())
.getReturnType());
retType = createUnionWithSubTypes(retType);
ConcreteType newret = type.toFunction().getReturnSlot()
.getType().unionWith(retType);
((ConcreteScope) type.getScope()).declareSlot(
ConcreteFunctionType.RETURN_SLOT_NAME, n, newret);
}
scope.declareSlot(lhs.getLastChild().getString(), n, type);
} else {
addActions(createAssignmentActions(lhs, n.getLastChild(), n));
}
break;
case Token.NEW:
case Token.CALL:
Node receiver = n.getFirstChild();
if (receiver.getType() == Token.GETPROP) {
Node first = receiver.getFirstChild();
// Special case the call() function.
if ("call".equals(first.getNext().getString())) {
if (first.getType() == Token.GETPROP) {
// foo.bar.call()
addAction(new FunctionCallBuilder(first, receiver.getNext())
.setPropName(first.getFirstChild().getNext().getString())
.setIsCallFunction()
.build());
} else {
// bar.call()
addAction(new FunctionCallBuilder(
first, receiver.getNext()).setIsCallFunction()
.build());
}
} else {
// foo.bar()
addAction(new FunctionCallBuilder(first, receiver.getNext())
.setPropName(first.getNext().getString())
.build());
}
} else {
// foo() or new Foo()
addAction(new FunctionCallBuilder(receiver, receiver.getNext())
.setIsNewCall(n.getType() == Token.NEW)
.build());
}
break;
case Token.NAME:
if (parent.getType() == Token.CATCH && parent.getFirstChild() == n) {
// The variable in a catch statement gets defined in the scope of
// the catch block. We approximate that, as does the normal type
// sytem, by declaring a variable for it in the scope in which the
// catch is declared.
scope.declareSlot(n.getString(), n,
createUnionWithSubTypes(
createType(getTypeRegistry().getType("Error")).toInstance()));
}
break;
case Token.RETURN:
if (n.getFirstChild() != null) {
addAction(new VariableAssignAction(
(ConcreteSlot) scope.getOwnSlot(
ConcreteFunctionType.RETURN_SLOT_NAME), n.getFirstChild()));
}
break;
}
Collection<Action> actions = getImplicitActions(n);
if (actions != null) {
for (Action action : actions) {
addAction(action);
}
}
}
}
|