Skip to content

Commit

Permalink
Add additional checks to Node::isEquivalentTo covering some node-pr…
Browse files Browse the repository at this point in the history
…ops that were ignored before.

Without this, some pretty semantically different snippets of code were considered equal in tests. Example, static and non-static class members with the same name. The new method of checking the props is also easier to expand.

Some tests are updated to conform to the tightened comparison.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=248049740
  • Loading branch information
nreid260 authored and EatingW committed May 14, 2019
1 parent 1ab5f4a commit 28d2eff
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 51 deletions.
30 changes: 20 additions & 10 deletions src/com/google/javascript/jscomp/Es6RewriteDestructuring.java
Expand Up @@ -427,11 +427,7 @@ private void replaceObjectPattern(
newRHS = defaultValueHook(getprop, defaultValue); newRHS = defaultValueHook(getprop, defaultValue);
} }
if (propsToDeleteForRest != null) { if (propsToDeleteForRest != null) {
Node propName = astFactory.createString(child.getString()); propsToDeleteForRest.add(child);
if (child.isQuotedString()) {
propName.setQuotedString();
}
propsToDeleteForRest.add(propName);
} }
} else if (child.isComputedProp()) { } else if (child.isComputedProp()) {
// const {[propExpr]: newLHS = defaultValue} = newRHS; // const {[propExpr]: newLHS = defaultValue} = newRHS;
Expand Down Expand Up @@ -562,11 +558,25 @@ private Node objectPatternRestRHS(
} }


private Node deletionNodeForRestProperty(Node restTempVarNameNode, Node property) { private Node deletionNodeForRestProperty(Node restTempVarNameNode, Node property) {
boolean useSquareBrackets = !property.isString() || property.isQuotedString(); final Node get;
Node get = switch (property.getToken()) {
useSquareBrackets case STRING_KEY:
? astFactory.createGetElem(restTempVarNameNode, property) get =
: astFactory.createGetProp(restTempVarNameNode, property.getString()); property.isQuotedString()
? astFactory.createGetElem(
restTempVarNameNode, astFactory.createString(property.getString()))
: astFactory.createGetProp(restTempVarNameNode, property.getString());
break;

case NAME:
get = astFactory.createGetElem(restTempVarNameNode, property);
break;

default:
throw new IllegalStateException(
"Unexpected property to delete node: " + property.toStringTree());
}

return astFactory.createDelProp(get); return astFactory.createDelProp(get);
} }


Expand Down
5 changes: 4 additions & 1 deletion src/com/google/javascript/jscomp/LateEs6ToEs3Converter.java
Expand Up @@ -209,9 +209,12 @@ private void visitObjectWithComputedProperty(Node obj) {
} else { } else {
Node val = propdef.removeFirstChild(); Node val = propdef.removeFirstChild();
JSType valueType = val.getJSType(); JSType valueType = val.getJSType();
Token token = propdef.isQuotedString() ? Token.GETELEM : Token.GETPROP;

propdef.setToken(Token.STRING); propdef.setToken(Token.STRING);
propdef.setJSType(stringType); propdef.setJSType(stringType);
Token token = propdef.isQuotedString() ? Token.GETELEM : Token.GETPROP; propdef.putBooleanProp(Node.QUOTED_PROP, false);

Node access = Node access =
withType(new Node(token, withType(IR.name(objName), objectType), propdef), valueType); withType(new Node(token, withType(IR.name(objName), objectType), propdef), valueType);
result = result =
Expand Down
64 changes: 31 additions & 33 deletions src/com/google/javascript/rhino/Node.java
Expand Up @@ -48,6 +48,7 @@
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.DoNotCall;
import com.google.javascript.rhino.StaticSourceFile.SourceKind; import com.google.javascript.rhino.StaticSourceFile.SourceKind;
import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSType;
Expand All @@ -62,6 +63,7 @@
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import javax.annotation.CheckReturnValue; import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable; import javax.annotation.Nullable;


Expand Down Expand Up @@ -1951,39 +1953,8 @@ public boolean isEquivalentTo(
return false; return false;
} }


if (token == Token.INC || token == Token.DEC) { for (Function<Node, Object> getter : PROP_GETTERS_FOR_EQUALITY) {
int post1 = this.getIntProp(Prop.INCRDECR); if (!Objects.equal(getter.apply(this), getter.apply(node))) {
int post2 = node.getIntProp(Prop.INCRDECR);
if (post1 != post2) {
return false;
}
} else if (token == Token.STRING || token == Token.STRING_KEY) {
if (token == Token.STRING_KEY) {
int quoted1 = this.getIntProp(Prop.QUOTED);
int quoted2 = node.getIntProp(Prop.QUOTED);
if (quoted1 != quoted2) {
return false;
}
}

int slashV1 = this.getIntProp(Prop.SLASH_V);
int slashV2 = node.getIntProp(Prop.SLASH_V);
if (slashV1 != slashV2) {
return false;
}
} else if (token == Token.CALL) {
if (this.getBooleanProp(Prop.FREE_CALL) != node.getBooleanProp(Prop.FREE_CALL)) {
return false;
}
} else if (token == Token.FUNCTION) {
// Must be the same kind of function to be equivalent
if (this.isArrowFunction() != node.isArrowFunction()) {
return false;
}
if (this.isGeneratorFunction() != node.isGeneratorFunction()) {
return false;
}
if (this.isAsyncFunction() != node.isAsyncFunction()) {
return false; return false;
} }
} }
Expand Down Expand Up @@ -2011,6 +1982,33 @@ public boolean isEquivalentTo(
return true; return true;
} }


/**
* Accessors for {@link Node} properties that should also be compared when comparing nodes for
* equality.
*
* <p>We'd prefer to list the props that should be ignored rather than the ones that should be
* checked, but that was too difficult initially. In general these properties are ones that show
* up as keywords / symbols which don't have their own nodes.
*
* <p>Accessor functions are used rather than {@link Prop}s to encode the correct way of reading
* the prop.
*/
private static final ImmutableList<Function<Node, Object>> PROP_GETTERS_FOR_EQUALITY =
ImmutableList.of(
Node::isArrowFunction,
Node::isAsyncFunction,
Node::isAsyncGeneratorFunction,
Node::isGeneratorFunction,
Node::isStaticMember,
Node::isYieldAll,
(n) -> n.getIntProp(Prop.SLASH_V),
(n) -> n.getIntProp(Prop.INCRDECR),
(n) -> n.getIntProp(Prop.QUOTED),
(n) -> n.getBooleanProp(Prop.FREE_CALL),
(n) -> n.getBooleanProp(Prop.COMPUTED_PROP_METHOD),
(n) -> n.getBooleanProp(Prop.COMPUTED_PROP_GETTER),
(n) -> n.getBooleanProp(Prop.COMPUTED_PROP_SETTER));

/** /**
* This function takes a set of GETPROP nodes and produces a string that is each property * This function takes a set of GETPROP nodes and produces a string that is each property
* separated by dots. If the node ultimately under the left sub-tree is not a simple name, this is * separated by dots. If the node ultimately under the left sub-tree is not a simple name, this is
Expand Down
Expand Up @@ -1313,14 +1313,14 @@ public void testTwoUnrelatedClasses_withMemberFns_ambiguated() {
} }


@Test @Test
public void testTwoUnrelatedClasses_withStaticMemberFns_ambiguatede() { public void testTwoUnrelatedClasses_withStaticMemberFns_ambiguated() {
test( test(
lines( lines(
"class Foo { static methodFoo() {} }", // "class Foo { static methodFoo() {} }", //
"class Bar { static methodBar() {} }"), "class Bar { static methodBar() {} }"),
lines( lines(
"class Foo { a() {} }", // "class Foo { static a() {} }", //
"class Bar { a() {} }")); "class Bar { static a() {} }"));
} }


@Test @Test
Expand Down
Expand Up @@ -3000,10 +3000,10 @@ public void testDisambiguateEs6ClassStaticMethods() {
"Bar.method();"), "Bar.method();"),
lines( lines(
"class Foo {", "class Foo {",
" _typeof_Foo_$method() {}", " static _typeof_Foo_$method() {}",
"}", "}",
"class Bar {", "class Bar {",
" _typeof_Bar_$method() {}", " static _typeof_Bar_$method() {}",
"}", "}",
"Foo._typeof_Foo_$method();", "Foo._typeof_Foo_$method();",
"Bar._typeof_Bar_$method();")); "Bar._typeof_Bar_$method();"));
Expand All @@ -3027,13 +3027,13 @@ public void testEs6ClassSideInheritedMethods_areDisambiguated() {
"Bar.method();"), "Bar.method();"),
lines( lines(
"class Foo {", "class Foo {",
" _typeof_Foo_$method() {}", " static _typeof_Foo_$method() {}",
"}", "}",
"class SubFoo extends Foo {", "class SubFoo extends Foo {",
" static _typeof_Foo_$method() {}", " static _typeof_Foo_$method() {}",
"}", "}",
"class Bar {", "class Bar {",
" _typeof_Bar_$method() {}", " static _typeof_Bar_$method() {}",
"}", "}",
"Foo._typeof_Foo_$method();", "Foo._typeof_Foo_$method();",
"SubFoo._typeof_Foo_$method();", "SubFoo._typeof_Foo_$method();",
Expand Down

0 comments on commit 28d2eff

Please sign in to comment.