Skip to content

Commit

Permalink
Fork the IS_CONSTANT_VAR prop into two flags for tracking whether the…
Browse files Browse the repository at this point in the history
… name was inferred to be a constant or was declared to be a constant.

This will allow some parts of the compiler to move side-effectful nodes past constant properties since we know they won't be modified by any side effect

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=255239136
  • Loading branch information
lukesandberg authored and blickly committed Jun 26, 2019
1 parent a60be1f commit 8357fc0
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 130 deletions.
3 changes: 2 additions & 1 deletion src/com/google/javascript/jscomp/AbstractVar.java
Expand Up @@ -129,7 +129,8 @@ public final boolean isDeclaredOrInferredConst() {
return false;
}

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

Expand Down
4 changes: 2 additions & 2 deletions src/com/google/javascript/jscomp/FunctionInjector.java
Expand Up @@ -680,8 +680,8 @@ private static void removeConstantVarAnnotation(Scope scope, String name) {
return;
}

if (nameNode.getBooleanProp(Node.IS_CONSTANT_VAR)) {
nameNode.removeProp(Node.IS_CONSTANT_VAR);
if (nameNode.isDeclaredConstantVar()) {
nameNode.setDeclaredConstantVar(false);
}
}

Expand Down
11 changes: 6 additions & 5 deletions src/com/google/javascript/jscomp/InferConsts.java
Expand Up @@ -64,18 +64,19 @@ private void considerVar(Var v, ReferenceCollection refCollection) {
Node nameNode = v.getNameNode();
JSDocInfo docInfo = v.getJSDocInfo();
if (docInfo != null && docInfo.isConstant()) {
nameNode.putBooleanProp(Node.IS_CONSTANT_VAR, true);
nameNode.setDeclaredConstantVar(true);
} else if (nameNode != null && nameNode.getParent().isConst()) {
nameNode.putBooleanProp(Node.IS_CONSTANT_VAR, true);
nameNode.setDeclaredConstantVar(true);
} else if (nameNode != null
&& compiler.getCodingConvention().isConstant(nameNode.getString())) {
nameNode.putBooleanProp(Node.IS_CONSTANT_VAR, true);
} else if (nameNode != null
nameNode.setDeclaredConstantVar(true);
}
if (nameNode != null
&& refCollection != null
&& refCollection.isWellDefined()
&& refCollection.isAssignedOnceInLifetime()
&& refCollection.firstReferenceIsAssigningDeclaration()) {
nameNode.putBooleanProp(Node.IS_CONSTANT_VAR, true);
nameNode.setInferredConstantVar(true);
}
}
}
Expand Up @@ -136,7 +136,7 @@ private void hoistConstantLikeField(Node clinitAssignment, Node topLevelDeclarat
declarationInClass.replaceChild(declarationAssignedValue, clinitAssignedValue);
compiler.reportChangeToEnclosingScope(topLevelDeclaration);
}
declarationInClass.putBooleanProp(Node.IS_CONSTANT_VAR, true);
declarationInClass.setDeclaredConstantVar(true);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -4433,7 +4433,8 @@ && getRootTarget(node).getGrandparent().isConst()) {
return true;
}

if (node.getBooleanProp(Node.IS_CONSTANT_VAR)) {
// TODO(lukes): does this actually care about things inferred to be constants?
if (node.isName() && (node.isDeclaredConstantVar() || node.isInferredConstantVar())) {
return true;
}

Expand Down
99 changes: 96 additions & 3 deletions src/com/google/javascript/rhino/Node.java
Expand Up @@ -99,6 +99,7 @@ private enum Prop {
// Function or constructor call side effect flags.
SIDE_EFFECT_FLAGS,
// The variable or property is constant.
// TODO(lukes): either document the differences or otherwise reconcile with CONSTANT_VAR_FLAGS
IS_CONSTANT_NAME,
// The variable creates a namespace.
IS_NAMESPACE,
Expand Down Expand Up @@ -135,8 +136,10 @@ private enum Prop {
EXPORT_DEFAULT,
// Set if an export is a "*"
EXPORT_ALL_FROM,
// A lexical variable is inferred const
IS_CONSTANT_VAR,
// A variable is inferred or declared as const meaning it is only ever assigned once at its
// declaration site.
// This is an int prop that holds a bitset of {@link ConstantVarFlags} values.
CONSTANT_VAR_FLAGS,
// Used by the ES6-to-ES3 translator.
IS_GENERATOR_MARKER,
// Used by the ES6-to-ES3 translator.
Expand Down Expand Up @@ -246,7 +249,6 @@ public final Node setNonJSDocComment(String comment) {
public static final Prop YIELD_ALL = Prop.YIELD_ALL;
public static final Prop EXPORT_DEFAULT = Prop.EXPORT_DEFAULT;
public static final Prop EXPORT_ALL_FROM = Prop.EXPORT_ALL_FROM;
public static final Prop IS_CONSTANT_VAR = Prop.IS_CONSTANT_VAR;
public static final Prop COMPUTED_PROP_METHOD = Prop.COMPUTED_PROP_METHOD;
public static final Prop COMPUTED_PROP_GETTER = Prop.COMPUTED_PROP_GETTER;
public static final Prop COMPUTED_PROP_SETTER = Prop.COMPUTED_PROP_SETTER;
Expand Down Expand Up @@ -2992,6 +2994,97 @@ private static boolean anyBitSet(int value, int mask) {
return (value & mask) != 0;
}

/**
* Constants for the {@link Prop#CONSTANT_VAR_FLAGS} bit set property.
*
* <ul>
* <li>{@link ConstantVarFlags#DECLARED} means the name was declared using annotation or syntax
* indicating it must be constant
* <li>{@link ConstantVarFlags#INFERRED} means the compiler can see that it is assigned exactly
* once, whether or not it was declared to be constant.
* </ul>
*
* <p>Either, both, or neither may be set for any name.
*/
private static final class ConstantVarFlags {
// each constant should be a distinct power of 2.

static final int DECLARED = 1;
static final int INFERRED = 2;

private ConstantVarFlags() {}
}

private final int getConstantVarFlags() {
return getIntProp(Prop.CONSTANT_VAR_FLAGS);
}

private final void setConstantVarFlag(int flag, boolean value) {
int flags = getConstantVarFlags();
if (value) {
flags = flags | flag;
} else {
flags = flags & ~flag;
}
putIntProp(Prop.CONSTANT_VAR_FLAGS, flags);
}

/**
* Returns whether this variable is declared as a constant.
*
* <p>The compiler considers a variable to be declared if:
*
* <ul>
* <li>it is declared with the {@code const} keyword, or
* <li>It is declared with a jsdoc {@code @const} annotation, or
* <li>The current coding convention considers it to be a constant.
* </ul>
*
* <p>Only valid to call on a {@linkplain #isName name} node.
*/
public final boolean isDeclaredConstantVar() {
checkState(isName(), "Should only be called on name nodes.");
return anyBitSet(getConstantVarFlags(), ConstantVarFlags.DECLARED);
}

/**
* Sets this variable to be a declared constant.
*
* <p>See {@link #isDeclaredConstantVar} for the rules.
*/
public final void setDeclaredConstantVar(boolean value) {
checkState(isName(), "Should only be called on name nodes.");
setConstantVarFlag(ConstantVarFlags.DECLARED, value);
}

/**
* Returns whether this variable is inferred to be constant.
*
* <p>The compiler infers a variable to be a constant if:
*
* <ul>
* <li>It is assigned at its declaration site, and
* <li>It is never reassigned during its lifetime, and
* <li>It is not defined by an extern.
* </ul>
*
* <p>Only valid to call on a {@linkplain #isName name} node.
*/
public final boolean isInferredConstantVar() {
checkState(isName(), "Should only be called on name nodes.");
return anyBitSet(getConstantVarFlags(), ConstantVarFlags.INFERRED);
}

/**
* Sets this variable to be an inferred constant. *
*
* <p>See {@link #isInferredConstantVar} for the rules.
*/
public final void setInferredConstantVar(boolean value) {
checkState(isName(), "Should only be called on name nodes.");
setConstantVarFlag(ConstantVarFlags.INFERRED, value);
}

/**
* This should only be called for STRING nodes children of OBJECTLIT.
*/
Expand Down

0 comments on commit 8357fc0

Please sign in to comment.