class TightenTypes.Action {
/** Returns all assignments that may occur by this action. */
Collection<Assignment> getAssignments(ConcreteScope scope);
}
class TightenTypes.VariableAssignAction {
public Collection<Assignment> getAssignments(ConcreteScope scope) {
return Lists.newArrayList(
new Assignment(slot, inferConcreteType(scope, expression)));
}
}
class TightenTypes.PropertyAssignAction {
/**
* Returns all assignments that could occur as a result of this property
* assign action. Each type in the receiver is checked for a property
* {@code propName}, and if that property exists, it is assigned the type
* of {@code expression}.
*/
public Collection<Assignment> getAssignments(ConcreteScope scope) {
ConcreteType recvType = inferConcreteType(scope, receiver);
ConcreteType exprType = inferConcreteType(scope, expression);
List<Assignment> assigns = Lists.newArrayList();
for (StaticSlot<ConcreteType> prop
: recvType.getPropertySlots(propName)) {
assigns.add(new Assignment((ConcreteSlot) prop, exprType));
}
return assigns;
}
}
class TightenTypes.ExternFunctionCall {
public Collection<Assignment> getAssignments(ConcreteScope scope) {
return getFunctionCallAssignments(inferConcreteType(scope, receiver),
thisType, argTypes);
}
}
class TightenTypes.FunctionCall {
public Collection<Assignment> getAssignments(ConcreteScope scope) {
ConcreteType thisType = ConcreteType.NONE;
ConcreteType recvType = inferConcreteType(scope, receiver);
// If a property name was specified, then the receiver is actually the
// type of this and the actual receiver is the type of that property.
if (propName != null) {
thisType = recvType;
recvType = thisType.getPropertyType(propName);
}
if (recvType.isAll()) {
// TODO(user): ensure that this will trigger for code like
// functions[3]();
throw new AssertionError(
"Found call on all type, which makes tighten types useless.");
}
// If this is a call to new, then a new instance of the receiver is
// created and passed in as the value of this.
if (isNewCall) {
thisType = ConcreteType.NONE;
for (ConcreteInstanceType instType
: recvType.getFunctionInstanceTypes()) {
thisType = thisType.unionWith(instType);
}
boolean added = allInstantiatedTypes.add(thisType);
if (added) {
// A new type instance invalidates the cached type intersections.
typeIntersectionMemos.clear();
}
}
List<ConcreteType> argTypes = Lists.newArrayList();
for (Node arg = firstArgument; arg != null; arg = arg.getNext()) {
argTypes.add(inferConcreteType(scope, arg));
}
return getFunctionCallAssignments(recvType, thisType, argTypes);
}
}
class TightenTypes.NativeCallFunctionCall {
public Collection<Assignment> getAssignments(ConcreteScope scope) {
ConcreteType thisType = (firstArgument != null)
? inferConcreteType(scope, firstArgument)
: getTopScope().getTypeOfThis();
ConcreteType recvType = inferConcreteType(scope, receiver);
if (recvType instanceof ConcreteInstanceType &&
((ConcreteInstanceType) recvType).isFunctionPrototype()) {
recvType = thisType.getPropertyType(propName);
}
List<ConcreteType> argTypes = Lists.newArrayList();
// Skip the first argument for call() as it is the 'this' object.
for (Node arg = firstArgument.getNext();
arg != null;
arg = arg.getNext()) {
argTypes.add(inferConcreteType(scope, arg));
}
return getFunctionCallAssignments(recvType, thisType, argTypes);
}
}
|