Skip to content

Commit

Permalink
Extract AbstractVar as a common superclass for Var and TypedVar.
Browse files Browse the repository at this point in the history
This is a preliminary step to making TypedScope no longer a subclass of Scope (and likewise for TypedVar and Var), which will happen in a future CL.  Like the parent CL extracting AbstractScope, this actually allows sharing more code between Var and TypedVar, since currently TypedVar shadows and reimplements most of Var's API.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=181401388
  • Loading branch information
shicks authored and brad4d committed Jan 10, 2018
1 parent 8a45ab3 commit 3bfc3f8
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 290 deletions.
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/AbstractScope.java
Expand Up @@ -35,7 +35,7 @@
* @see NodeTraversal * @see NodeTraversal
* *
*/ */
public abstract class AbstractScope<S extends AbstractScope<S, V>, V extends Var> abstract class AbstractScope<S extends AbstractScope<S, V>, V extends AbstractVar<S, V>>
implements StaticScope, Serializable { implements StaticScope, Serializable {
final Map<String, V> vars = new LinkedHashMap<>(); final Map<String, V> vars = new LinkedHashMap<>();
S parent; S parent;
Expand Down
224 changes: 224 additions & 0 deletions src/com/google/javascript/jscomp/AbstractVar.java
@@ -0,0 +1,224 @@
/*
* Copyright 2018 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.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticRef;
import com.google.javascript.rhino.StaticSlot;
import com.google.javascript.rhino.StaticSourceFile;
import com.google.javascript.rhino.Token;

/**
* Used by {@code Scope} to store information about variables.
*/
public class AbstractVar<S extends AbstractScope<S, V>, V extends AbstractVar<S, V>>
implements StaticSlot, StaticRef {

final String name;

/** Var node */
final Node nameNode;

/** Input source */
final CompilerInput input;

/**
* The index at which the var is declared. e.g. if it's 0, it's the first
* declared variable in that scope
*/
final int index;

/** The enclosing scope */
final S scope;

AbstractVar(String name, Node nameNode, S scope, int index, CompilerInput input) {
this.name = name;
this.nameNode = nameNode;
this.scope = scope;
this.index = index;
this.input = input;
}

// Non-final for jsdev tests
@Override
public String getName() {
return name;
}

@Override
public final Node getNode() {
return nameNode;
}

final CompilerInput getInput() {
return input;
}

@Override
public StaticSourceFile getSourceFile() {
return nameNode.getStaticSourceFile();
}

// Temporarily non-final for TypedVar
@Override
public V getSymbol() {
return thisVar();
}

// Temporarily non-final for TypedVar
@Override
public V getDeclaration() {
return nameNode == null ? null : thisVar();
}

public final Node getParentNode() {
return nameNode == null ? null : nameNode.getParent();
}

/**
* Whether this is a bleeding function (an anonymous named function
* that bleeds into the inner scope).
*/
public boolean isBleedingFunction() {
return NodeUtil.isFunctionExpression(getParentNode());
}

// Temporarily non-final for TypedVar
public S getScope() {
return scope;
}

// Non-final for jsdev tests
public boolean isGlobal() {
return scope.isGlobal();
}

public final boolean isLocal() {
return scope.isLocal();
}

final boolean isExtern() {
return input == null || input.isExtern();
}

/**
* Returns {@code true} if the variable is declared as a constant,
* based on the value reported by {@code NodeUtil}.
*/
public final boolean isInferredConst() {
if (nameNode == null) {
return false;
}

return nameNode.getBooleanProp(Node.IS_CONSTANT_VAR)
|| nameNode.getBooleanProp(Node.IS_CONSTANT_NAME);
}

/**
* Returns {@code true} if the variable is declared as a define.
* A variable is a define if it is annotated by {@code @define}.
*/
public final boolean isDefine() {
JSDocInfo info = getJSDocInfo();
return info != null && info.isDefine();
}

public final Node getInitialValue() {
return NodeUtil.getRValueOfLValue(nameNode);
}

// Non-final for jsdev tests
public Node getNameNode() {
return nameNode;
}

// Non-final for jsdev tests
@Override
public JSDocInfo getJSDocInfo() {
return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode);
}

final boolean isVar() {
return declarationType() == Token.VAR;
}

final boolean isCatch() {
return declarationType() == Token.CATCH;
}

final boolean isLet() {
return declarationType() == Token.LET;
}

final boolean isConst() {
return declarationType() == Token.CONST;
}

final boolean isClass() {
return declarationType() == Token.CLASS;
}

final boolean isParam() {
return declarationType() == Token.PARAM_LIST;
}

public final boolean isDefaultParam() {
Node parent = nameNode.getParent();
return parent.getParent().isParamList() && parent.isDefaultValue()
&& parent.getFirstChild() == nameNode;
}

final boolean isImport() {
return declarationType() == Token.IMPORT;
}

public boolean isArguments() {
return false;
}

private static final ImmutableSet<Token> DECLARATION_TYPES = Sets.immutableEnumSet(
Token.VAR,
Token.LET,
Token.CONST,
Token.FUNCTION,
Token.CLASS,
Token.CATCH,
Token.IMPORT,
Token.PARAM_LIST);

protected Token declarationType() {
for (Node current = nameNode; current != null;
current = current.getParent()) {
if (DECLARATION_TYPES.contains(current.getToken())) {
return current.getToken();
}
}
throw new IllegalStateException("The nameNode for " + this + " must be a descendant"
+ " of one of: " + DECLARATION_TYPES);
}

// This is safe because any concrete subclass of AbstractVar<V> should be assignable to V.
// While it's theoretically possible to do otherwise, such a class would be very awkward to
// implement, and is therefore not worth worrying about.
@SuppressWarnings("unchecked")
private V thisVar() {
return (V) this;
}
}
Expand Up @@ -64,7 +64,7 @@ public Iterable<TypedVar> getReferences(TypedVar var) {


@Override @Override
public TypedScope getScope(TypedVar var) { public TypedScope getScope(TypedVar var) {
return var.scope; return (TypedScope) var.scope;
} }


@Override @Override
Expand Down
107 changes: 5 additions & 102 deletions src/com/google/javascript/jscomp/TypedVar.java
Expand Up @@ -17,22 +17,17 @@
package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticSourceFile;
import com.google.javascript.rhino.TypeI; import com.google.javascript.rhino.TypeI;
import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.StaticTypedRef; import com.google.javascript.rhino.jstype.StaticTypedRef;
import com.google.javascript.rhino.jstype.StaticTypedSlot; import com.google.javascript.rhino.jstype.StaticTypedSlot;


/** /**
* Several methods in this class, such as {@code isVar} throw an exception when called, and several * {@link AbstractVar} subclass for use with {@link TypedScope}.
* 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> { public class TypedVar extends Var implements StaticTypedSlot<JSType>, StaticTypedRef<JSType> {


final TypedScope scope;
private JSType type; private JSType type;
// The next two fields and the associated methods are only used by // 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. // TypeInference.java. Maybe there is a way to avoid having them in all typed variable instances.
Expand All @@ -50,94 +45,22 @@ public class TypedVar extends Var implements StaticTypedSlot<JSType>, StaticType
TypedScope scope, int index, CompilerInput input) { TypedScope scope, int index, CompilerInput input) {
super(name, nameNode, scope, index, input); super(name, nameNode, scope, index, input);
this.type = type; this.type = type;
this.scope = scope;
this.typeInferred = inferred; 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 @Override
public TypedVar getSymbol() { public TypedVar getSymbol() {
return this; return (TypedVar) super.getSymbol();
} }


@Override @Override
public TypedVar getDeclaration() { public TypedVar getDeclaration() {
return nameNode == null ? null : this; return (TypedVar) super.getDeclaration();
}

@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 @Override
public TypedScope getScope() { public TypedScope getScope() {
return scope; return (TypedScope) super.getScope();
}

@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);
} }


/** /**
Expand All @@ -160,7 +83,7 @@ void setType(JSType type) {


void resolveType(ErrorReporter errorReporter) { void resolveType(ErrorReporter errorReporter) {
if (type != null) { if (type != null) {
type = type.resolve(errorReporter, scope); type = type.resolve(errorReporter, (TypedScope) scope);
} }
} }


Expand Down Expand Up @@ -213,24 +136,4 @@ void markAssignedExactlyOnce() {
boolean isMarkedAssignedExactlyOnce() { boolean isMarkedAssignedExactlyOnce() {
return markedAssignedExactlyOnce; 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.");
}
} }

0 comments on commit 3bfc3f8

Please sign in to comment.