Skip to content

Commit

Permalink
Add equivalence check for structural interfaces
Browse files Browse the repository at this point in the history
Given two structurally compatible @records: A and B
previously we would say they were not equivalent since
A and B had different names, but they are actually equivalent.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=99965953
  • Loading branch information
JacksonGL authored and blickly committed Aug 5, 2015
1 parent 03a7873 commit 0df668a
Show file tree
Hide file tree
Showing 10 changed files with 515 additions and 88 deletions.
7 changes: 4 additions & 3 deletions src/com/google/javascript/rhino/jstype/ArrowType.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -211,9 +211,10 @@ boolean hasEqualParameters(ArrowType that, EquivalenceMethod eqMethod) {
} }


boolean checkArrowEquivalenceHelper( boolean checkArrowEquivalenceHelper(
ArrowType that, EquivalenceMethod eqMethod) { ArrowType that, EquivalenceMethod eqMethod, EqCache eqCache) {
// Please keep this method in sync with the hashCode() method below. // Please keep this method in sync with the hashCode() method below.
if (!returnType.checkEquivalenceHelper(that.returnType, eqMethod)) { if (!returnType.checkEquivalenceHelper(
that.returnType, eqMethod, eqCache)) {
return false; return false;
} }
return hasEqualParameters(that, eqMethod); return hasEqualParameters(that, eqMethod);
Expand Down Expand Up @@ -318,4 +319,4 @@ private boolean hasTemplatedParameterType() {
} }
return false; return false;
} }
} }
67 changes: 62 additions & 5 deletions src/com/google/javascript/rhino/jstype/FunctionType.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ public ObjectType getTopMostDefiningType(String propertyName) {
* have signatures, two interfaces are equal if their names match. * have signatures, two interfaces are equal if their names match.
*/ */
boolean checkFunctionEquivalenceHelper( boolean checkFunctionEquivalenceHelper(
FunctionType that, EquivalenceMethod eqMethod) { FunctionType that, EquivalenceMethod eqMethod, EqCache eqCache) {
if (isConstructor()) { if (isConstructor()) {
if (that.isConstructor()) { if (that.isConstructor()) {
return this == that; return this == that;
Expand All @@ -960,16 +960,62 @@ boolean checkFunctionEquivalenceHelper(
} }
if (isInterface()) { if (isInterface()) {
if (that.isInterface()) { if (that.isInterface()) {
return getReferenceName().equals(that.getReferenceName()); if (getReferenceName().equals(that.getReferenceName())) {
return true;
} else {
if (this.isStructuralInterface()
&& that.isStructuralInterface()) {
return checkStructuralInterfaceEquivalenceHelper(
that, eqMethod, eqCache);
}
return false;
}
} }
return false; return false;
} }
if (that.isInterface()) { if (that.isInterface()) {
return false; return false;
} }


return typeOfThis.checkEquivalenceHelper(that.typeOfThis, eqMethod) && return typeOfThis.checkEquivalenceHelper(that.typeOfThis, eqMethod, eqCache) &&
call.checkArrowEquivalenceHelper(that.call, eqMethod); call.checkArrowEquivalenceHelper(that.call, eqMethod, eqCache);
}

boolean checkStructuralInterfaceEquivalenceHelper(
final JSType that, EquivalenceMethod eqMethod, EqCache eqCache) {
Preconditions.checkState(eqCache.isStructuralTyping());
Preconditions.checkState(this.isStructuralInterface());
Preconditions.checkState(that.isRecordType() || that.isFunctionType());

MatchStatus result = eqCache.checkCache(this, that);
if (result != null) {
return result.subtypeValue();
}

if (this.hasAnyTemplateTypes() || that.hasAnyTemplateTypes()) {
return false;
}
Map<String, JSType> thisPropList = getPropertyTypeMap(this);
Map<String, JSType> thatPropList = that.isRecordType()
? that.toMaybeRecordType().getOwnPropertyTypeMap()
: getPropertyTypeMap(that.toMaybeFunctionType());

if (thisPropList.size() != thatPropList.size()) {
eqCache.updateCache(this, that, MatchStatus.NOT_MATCH);
return false;
}
for (String propName : thisPropList.keySet()) {
JSType typeInInterface = thisPropList.get(propName);
JSType typeInFunction = thatPropList.get(propName);
if (typeInFunction == null
|| !typeInFunction.checkEquivalenceHelper(
typeInInterface, eqMethod, eqCache)) {
eqCache.updateCache(this, that, MatchStatus.NOT_MATCH);
return false;
}
}
eqCache.updateCache(this, that, MatchStatus.MATCH);
return true;
} }


@Override @Override
Expand All @@ -979,7 +1025,12 @@ public int hashCode() {


public boolean hasEqualCallType(FunctionType otherType) { public boolean hasEqualCallType(FunctionType otherType) {
return this.call.checkArrowEquivalenceHelper( return this.call.checkArrowEquivalenceHelper(
otherType.call, EquivalenceMethod.IDENTITY); otherType.call, EquivalenceMethod.IDENTITY, EqCache.create());
}

public boolean hasEqualCallType(FunctionType otherType, EqCache eqCache) {
return this.call.checkArrowEquivalenceHelper(
otherType.call, EquivalenceMethod.IDENTITY, eqCache);
} }


/** /**
Expand Down Expand Up @@ -1403,10 +1454,16 @@ public void setImplicitMatch(boolean flag) {
isStructuralInterface = flag; isStructuralInterface = flag;
} }


@Override
public boolean isStructuralInterface() { public boolean isStructuralInterface() {
return isInterface() && isStructuralInterface; return isInterface() && isStructuralInterface;
} }


@Override
public boolean isStructuralType() {
return isStructuralInterface();
}

/** /**
* get the map of properties to types covered in a function type * get the map of properties to types covered in a function type
* @return a Map that maps the property's name to the property's type * @return a Map that maps the property's name to the property's type
Expand Down
Loading

0 comments on commit 0df668a

Please sign in to comment.