/*
* Copyright 2015 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticSourceFile;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.StaticTypedRef;
import com.google.javascript.rhino.jstype.StaticTypedSlot;
/**
* Several methods in this class, such as {@code isVar} throw an exception when called, and several
* methods are currently identical to the ones in Var. The reason for this is that we want to shadow
* methods from the parent class, to avoid calling them accidentally.
*/
public class TypedVar extends Var implements StaticTypedSlot<JSType>, StaticTypedRef<JSType> {
final TypedScope scope;
private JSType type;
// The next two fields and the associated methods are only used by
// TypeInference.java. Maybe there is a way to avoid having them in all typed variable instances.
private boolean markedEscaped = false;
private boolean markedAssignedExactlyOnce = false;
/**
* Whether the variable's type has been inferred or is declared. An inferred
* type may change over time (as more code is discovered), whereas a
* declared type is a static contract that must be matched.
*/
private final boolean typeInferred;
TypedVar(boolean inferred, String name, Node nameNode, JSType type,
TypedScope scope, int index, CompilerInput input) {
super(name, nameNode, scope, index, input);
this.type = type;
this.scope = scope;
this.typeInferred = inferred;
}
@Override
public String getName() {
return name;
}
@Override
public Node getNode() {
return nameNode;
}
@Override
CompilerInput getInput() {
return input;
}
@Override
public StaticSourceFile getSourceFile() {
return nameNode.getStaticSourceFile();
}
@Override
public TypedVar getSymbol() {
return this;
}
@Override
public TypedVar getDeclaration() {
return nameNode == null ? null : this;
}
@Override
public Node getParentNode() {
return nameNode == null ? null : nameNode.getParent();
}
@Override
public boolean isBleedingFunction() {
throw new IllegalStateException(
"Method isBleedingFunction cannot be called on typed variables.");
}
@Override
public TypedScope getScope() {
return scope;
}
@Override
public boolean isGlobal() {
return scope.isGlobal();
}
@Override
public boolean isLocal() {
return scope.isLocal();
}
@Override
boolean isExtern() {
return input == null || input.isExtern();
}
@Override
public boolean isInferredConst() {
throw new IllegalStateException("Method isInferredConst cannot be called on typed variables.");
}
@Override
public boolean isDefine() {
throw new IllegalStateException("Method isDefine cannot be called on typed variables.");
}
@Override
public Node getInitialValue() {
return NodeUtil.getRValueOfLValue(nameNode);
}
@Override
public Node getNameNode() {
return nameNode;
}
@Override
public JSDocInfo getJSDocInfo() {
return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode);
}
/**
* Gets this variable's type. To know whether this type has been inferred,
* see {@code #isTypeInferred()}.
*/
@Override
public JSType getType() {
return type;
}
void setType(JSType type) {
this.type = type;
}
void resolveType(ErrorReporter errorReporter) {
if (type != null) {
type = type.resolve(errorReporter, scope);
}
}
/**
* Returns whether this variable's type is inferred. To get the variable's
* type, see {@link #getType()}.
*/
@Override
public boolean isTypeInferred() {
return typeInferred;
}
public String getInputName() {
if (input == null) {
return "<non-file>";
}
return input.getName();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof TypedVar)) {
return false;
}
return ((TypedVar) other).nameNode == nameNode;
}
@Override
public int hashCode() {
return nameNode.hashCode();
}
@Override
public String toString() {
return "Var " + name + "{" + type + "}";
}
void markEscaped() {
markedEscaped = true;
}
boolean isMarkedEscaped() {
return markedEscaped;
}
void markAssignedExactlyOnce() {
markedAssignedExactlyOnce = true;
}
boolean isMarkedAssignedExactlyOnce() {
return markedAssignedExactlyOnce;
}
@Override
boolean isVar() {
throw new IllegalStateException("Method isVar cannot be called on typed variables.");
}
@Override
boolean isLet() {
throw new IllegalStateException("Method isLet cannot be called on typed variables.");
}
@Override
boolean isConst() {
throw new IllegalStateException("Method isConst cannot be called on typed variables.");
}
@Override
boolean isParam() {
throw new IllegalStateException("Method isParam cannot be called on typed variables.");
}
}