use of com.google.javascript.rhino.jstype.StaticTypedSlot in project closure-compiler by google.
the class TypeInference method getPropertyType.
private JSType getPropertyType(JSType objType, String propName, Node n, FlowScope scope) {
// We often have a couple of different types to choose from for the
// property. Ordered by accuracy, we have
// 1) A locally inferred qualified name (which is in the FlowScope)
// 2) A globally declared qualified name (which is in the FlowScope)
// 3) A property on the owner type (which is on objType)
// 4) A name in the type registry (as a last resort)
JSType propertyType = null;
boolean isLocallyInferred = false;
// Scopes sometimes contain inferred type info about qualified names.
String qualifiedName = n.getQualifiedName();
StaticTypedSlot var = qualifiedName != null ? scope.getSlot(qualifiedName) : null;
if (var != null) {
JSType varType = var.getType();
if (varType != null) {
boolean isDeclared = !var.isTypeInferred();
isLocallyInferred = (var != getDeclaredVar(scope, qualifiedName));
if (isDeclared || isLocallyInferred) {
propertyType = varType;
}
}
}
if (propertyType == null && objType != null) {
JSType foundType = objType.findPropertyType(propName);
if (foundType != null) {
propertyType = foundType;
}
}
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(scope.getDeclarationScope(), qualifiedName));
if (regType != null) {
propertyType = regType.getConstructor();
}
}
if (propertyType == null) {
return unknownType;
} else if (propertyType.equals(unknownType) && isLocallyInferred) {
// then use CHECKED_UNKNOWN_TYPE instead to indicate that.
return getNativeType(CHECKED_UNKNOWN_TYPE);
} else {
return propertyType;
}
}
use of com.google.javascript.rhino.jstype.StaticTypedSlot in project closure-compiler by google.
the class TypeInference method traverseName.
private FlowScope traverseName(Node n, FlowScope scope) {
String varName = n.getString();
Node value = n.getFirstChild();
JSType type = n.getJSType();
if (value != null) {
// The only case where `value` isn't null is when we are in a name declaration/initialization
// var x = 3;
scope = traverse(value, scope);
return updateScopeForAssignment(scope, n, getJSType(value), AssignmentType.DECLARATION);
}
if (NodeUtil.isNameDeclaration(n.getParent()) && isPossibleMixinApplication(n, /* rvalue= */
null)) {
addMissingInterfaceProperties(type);
}
if (n.getParent().isLet()) {
// Whenever we see a LET, we're guaranteed it's not yet in the scope, and we don't need to
// worry about it being from an outer scope. In this case, it has no child, so the actual
// type should be undefined, but we make a special allowance for type-annotated variables.
// In that case, we use the annotated type instead.
// TODO(sdh): I would have thought that #updateScopeForTypeChange would handle using the
// declared type correctly, but for some reason it doesn't so we handle it here.
JSType resultType = type != null ? type : getNativeType(VOID_TYPE);
scope = updateScopeForAssignment(scope, n, resultType, AssignmentType.DECLARATION);
type = resultType;
} else {
StaticTypedSlot var = scope.getSlot(varName);
if (var != null) {
// There are two situations where we don't want to use type information
// from the scope, even if we have it.
// 1) The var is escaped and assigned in an inner scope, e.g.,
// function f() { var x = 3; function g() { x = null } (x); }
boolean isInferred = var.isTypeInferred();
boolean unflowable = isInferred && isUnflowable(getDeclaredVar(scope, varName));
// 2) We're reading type information from another scope for an
// inferred variable. That variable is assigned more than once,
// and we can't know which type we're getting.
//
// var t = null; function f() { (t); } doStuff(); t = {};
//
// Notice that this heuristic isn't perfect. For example, you might
// have:
//
// function f() { (t); } f(); var t = 3;
//
// In this case, we would infer the first reference to t as
// type {number}, even though it's undefined.
TypedVar maybeOuterVar = isInferred && containerScope.isLocal() ? containerScope.getParent().getVar(varName) : null;
boolean nonLocalInferredSlot = var.equals(maybeOuterVar) && !maybeOuterVar.isMarkedAssignedExactlyOnce();
if (!unflowable && !nonLocalInferredSlot) {
type = var.getType();
if (type == null) {
type = unknownType;
}
}
}
}
n.setJSType(type);
return scope;
}
use of com.google.javascript.rhino.jstype.StaticTypedSlot in project closure-compiler by google.
the class TypeTransformation method evalTypeOfVar.
private JSType evalTypeOfVar(Node ttlAst) {
String name = getCallArgument(ttlAst, 0).getString();
StaticTypedSlot slot = typeEnv.getSlot(name);
JSType type = slot != null ? slot.getType() : null;
if (type == null) {
reportWarning(ttlAst, VAR_UNDEFINED, name);
return getUnknownType();
}
return type;
}
use of com.google.javascript.rhino.jstype.StaticTypedSlot in project closure-compiler by google.
the class TypeTransformation method getType.
private JSType getType(String typeName) {
JSType type = registry.getType(typeEnv, typeName);
if (type != null) {
return type;
}
StaticTypedSlot slot = typeEnv.getSlot(typeName);
type = slot != null ? slot.getType() : null;
if (type != null) {
if (type.isConstructor() || type.isInterface()) {
return type.toMaybeFunctionType().getInstanceType().getRawType();
}
if (type.isEnumElementType()) {
return type.getEnumeratedTypeOfEnumElement();
}
return type;
}
JSDocInfo jsdoc = slot == null ? null : slot.getJSDocInfo();
if (jsdoc != null && jsdoc.hasTypedefType()) {
return this.registry.evaluateTypeExpression(jsdoc.getTypedefType(), typeEnv);
}
return null;
}
use of com.google.javascript.rhino.jstype.StaticTypedSlot in project closure-compiler by google.
the class SemanticReverseAbstractInterpreter method caseAndOrMaybeShortCircuiting.
@CheckReturnValue
private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, Outcome outcome) {
// Perform two separate refinements, one for if short-circuiting occurred, and one for if it did
// not. Because it's not clear whether short-circuiting occurred, we actually have to ignore
// both separate result flow scopes individually, but if they both refined the same slot, we
// can join the two refinements. TODO(sdh): look into simplifying this. If joining were
// more efficient, we should just be able to join the scopes unconditionally?
Set<String> refinements = new HashSet<>();
blindScope = new RefinementTrackingFlowScope(blindScope, refinements);
FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome(left, blindScope, outcome.not());
StaticTypedSlot leftVar = refinements.size() == 1 ? leftScope.getSlot(refinements.iterator().next()) : null;
if (leftVar == null) {
// must create a child instead.
return unwrap(blindScope);
}
refinements.clear();
// Note: re-wrap the scope, in case it was unwrapped by a nested call to this method.
FlowScope rightScope = new RefinementTrackingFlowScope(firstPreciserScopeKnowingConditionOutcome(left, blindScope, outcome), refinements);
rightScope = firstPreciserScopeKnowingConditionOutcome(right, rightScope, outcome.not());
StaticTypedSlot rightVar = refinements.size() == 1 ? rightScope.getSlot(refinements.iterator().next()) : null;
if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) {
return unwrap(blindScope);
}
JSType type = leftVar.getType().getLeastSupertype(rightVar.getType());
return unwrap(blindScope).inferSlotType(leftVar.getName(), type);
}
Aggregations