Skip to content

Commit

Permalink
JBIDE-21036 Infinite cycle in ParametedType.isAssignableTo()
Browse files Browse the repository at this point in the history
  • Loading branch information
scabanovich committed Nov 13, 2015
1 parent 5143ce8 commit fcba7dd
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009 Red Hat, Inc.
* Copyright (c) 2009,2015 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
Expand Down Expand Up @@ -155,9 +155,14 @@ public void setPositionProvider(PositionProvider p) {
provider = p;
}

@Override
public boolean equals(Object object) {
if(!(object instanceof ParametedType)) return false;
ParametedType other = (ParametedType)object;
return equals(other, null, iterationsLimit);
}

boolean equals(ParametedType other, Set<String> underConsideration,int depth) {
if(signature != null && signature.equals(other.signature)) {
return true;
}
Expand All @@ -167,14 +172,35 @@ public boolean equals(Object object) {
if(parameterTypes.size() != other.parameterTypes.size()) {
return false;
}
for (int i = 0; i < parameterTypes.size(); i++) {
if(!parameterTypes.get(i).equals(other.parameterTypes.get(i))) {
return false;
}
}
if(arrayIndex != other.arrayIndex) {
return false;
}
String code = null;
if(!parameterTypes.isEmpty()) {
if(depth <= 0) {
if(!equalsFailReported) {
reportFail("ParametedType.equals()", other, underConsideration);
equalsFailReported = true;
}
return false;
}
code = "" + getSignature() + "<-" + other.getSignature();
if(underConsideration == null) {
underConsideration = new HashSet<String>();
} else if(underConsideration.contains(code)) {
//Cycle is prevented, this case is already considered up stack.
return true;
}
underConsideration.add(code);
for (int i = 0; i < parameterTypes.size(); i++) {
ParametedType ithis = parameterTypes.get(i);
ParametedType iother = other.parameterTypes.get(i);
if(!ithis.equals(iother, underConsideration, depth - 1)) {
return false;
}
}
underConsideration.remove(code);
}

return true;
}
Expand Down Expand Up @@ -380,15 +406,16 @@ Set<IParametedType> buildAllTypes(Set<String> processed, ParametedType p, Set<IP
return types;
}

@Override
public String toString() {
return signature + ":" + super.toString(); //$NON-NLS-1$
}

public boolean isAssignableTo(ParametedType other, boolean checkInheritance) {
return isAssignableTo(other, checkInheritance, new HashMap<String, IType>());
return isAssignableTo(other, checkInheritance, new HashMap<String, IType>(), null, iterationsLimit);
}

boolean isAssignableTo(ParametedType other, boolean checkInheritance, Map<String,IType> resolvedVars) {
private boolean isAssignableTo(ParametedType other, boolean checkInheritance, Map<String,IType> resolvedVars, Set<String> underConsideration, int depth) {
if(equals(other)) return true;
if("*".equals(other.getSignature())) { //$NON-NLS-1$
return true;
Expand All @@ -406,18 +433,54 @@ boolean isAssignableTo(ParametedType other, boolean checkInheritance, Map<String
}
return true;
}
String code = (!parameterTypes.isEmpty() || !other.parameterTypes.isEmpty()) ? "" + getSignature() + "<-" + other.getSignature() : null;
if(underConsideration != null && underConsideration.contains(code)) {
//Cycle is prevented, this case is already considered up stack.
return true;
}
if(this.type.equals(other.type)) {
if(areTypeParametersAssignableTo(other, resolvedVars)) return true;
if(code != null) {
if(underConsideration == null) {
underConsideration = new HashSet<String>();
}
underConsideration.add(code);
}
if(areTypeParametersAssignableTo(other, resolvedVars, underConsideration, depth)) {
if(code != null) {
underConsideration.remove(code);
}
return true;
}
}
if(checkInheritance) {
for (IParametedType t: getInheritedTypes()) {
if(((ParametedType)t).isAssignableTo(other, false, resolvedVars)) return true;
Collection<IParametedType> ts = getAllTypes();
if(!ts.isEmpty() && (code != null)) {
if(code != null) {
if(underConsideration == null) {
underConsideration = new HashSet<String>();
}
underConsideration.add(code);
}
}
for (IParametedType t: ts) {
if(t == this) {
continue;
}
if(((ParametedType)t).isAssignableTo(other, false, resolvedVars, underConsideration, depth)) {
if(code != null) {
underConsideration.remove(code);
}
return true;
}
}
}
if(code != null && underConsideration != null) {
underConsideration.remove(code);
}
return false;
}

boolean areTypeParametersAssignableTo(ParametedType other, Map<String,IType> resolvedVars) {
private boolean areTypeParametersAssignableTo(ParametedType other, Map<String,IType> resolvedVars, Set<String> underConsideration,int depth) {
if(other.parameterTypes.isEmpty()) return true;
if(this.parameterTypes.isEmpty()) {
for (ParametedType p2: other.parameterTypes) {
Expand All @@ -433,26 +496,33 @@ boolean areTypeParametersAssignableTo(ParametedType other, Map<String,IType> res
return true;
}
if(this.parameterTypes.size() != other.parameterTypes.size()) return false;
if(depth <= 0) {
if(!isAssignableFailReported) {
reportFail("ParametedType.isAssignableTo()", other, underConsideration);
isAssignableFailReported = true;
}
return false;
}
for (int i = 0; i < parameterTypes.size(); i++) {
ParametedType p1 = parameterTypes.get(i);
ParametedType p2 = other.parameterTypes.get(i);
if(p1.isLower() || (p1.isUpper() && !p1.isVariable)) return false;
if(p1.isVariable()) {
if(p2.isVariable()) {
if(p2.isAssignableTo(p1, true, resolvedVars)) continue;
if(p2.isAssignableTo(p1, true, resolvedVars, underConsideration, depth - 1)) continue;
} else if(p2.isLower()) {
if(p2.isAssignableTo(p1, true, resolvedVars)) continue;
if(p2.isAssignableTo(p1, true, resolvedVars, underConsideration, depth - 1)) continue;
} else if(p2.isUpper()) {
if(p2.isAssignableTo(p1, true, resolvedVars)) continue;
if(p1.isAssignableTo(p2, true, resolvedVars)) continue;
if(p2.isAssignableTo(p1, true, resolvedVars, underConsideration, depth - 1)) continue;
if(p1.isAssignableTo(p2, true, resolvedVars, underConsideration, depth - 1)) continue;
} else {
if(p2.isAssignableTo(p1, true, resolvedVars)) continue;
if(p2.isAssignableTo(p1, true, resolvedVars, underConsideration, depth - 1)) continue;
}
} else {
if(p2.isLower()) {
if(p2.isAssignableTo(p1, true, resolvedVars)) continue;
if(p2.isAssignableTo(p1, true, resolvedVars, underConsideration, depth - 1)) continue;
} else {
if(p1.isAssignableTo(p2, true, resolvedVars)) continue;
if(p1.isAssignableTo(p2, true, resolvedVars, underConsideration, depth - 1)) continue;
}
}

Expand Down Expand Up @@ -500,4 +570,21 @@ public long getInheritanceCode() {
}
return inheritanceHashcode;
}

static final int iterationsLimit = 10;
static boolean equalsFailReported = false;
static boolean isAssignableFailReported = false;

private void reportFail(String name, ParametedType other, Set<String> underConsideration) {
String message = "Method " + name + " failed to complete reaching the limit of " + iterationsLimit + " iterations when processing types:";
StringBuilder sb = new StringBuilder(message);
sb.append("\n").append(getSignature()).append(" and ").append(other.getSignature()).append("\n");
if(underConsideration != null && !underConsideration.isEmpty()) {
sb.append("Previous iterations processed types:").append("\n");
for (String s: underConsideration) {
sb.append("\t").append(s).append("\n");
}
}
CommonCorePlugin.getPluginLog().logError(sb.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>JavaProject</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package parameters;

public class Dependencies {

static class Type1<V extends Type1<V>> {
}

static class Type2<T extends Type2<T>> extends Type1<Type2<T>> {
}

static class Type3 extends Type2<Type3> {
}

Type1<Type2<Type3>> f1;

static class Type4<V extends Type4.Type5<V>> {

static class Type5<V1 extends Type4.Type5<V1>> {
}

Type4.Type5<V> f2;

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*******************************************************************************
* Copyright (c) 2015 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.core.test;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.jboss.tools.common.java.IParametedType;
import org.jboss.tools.common.java.ParametedType;
import org.jboss.tools.common.java.ParametedTypeFactory;
import org.jboss.tools.common.util.EclipseJavaUtil;
import org.jboss.tools.test.util.ResourcesUtils;
import org.junit.Before;
import org.junit.Test;

public class ParametedTypeTest {

IProject project;

@Before
public void setUp() throws Exception {
if(project == null) {
project = importProject("JavaProject");
}
}

private static IProject importProject(String name) throws Exception {
IProject project = ResourcesUtils.importProject("org.jboss.tools.common.core.test", "projects/" + name);
assertNotNull(project);
assertTrue(project.exists());
return project;
}

@Test
public void testEquals() throws Exception {
IJavaProject javaProject = JavaCore.create(project);
assertNotNull(javaProject);

IType dependencies = EclipseJavaUtil.findType(javaProject, "parameters.Dependencies");
assertNotNull(dependencies);
IType type4 = EclipseJavaUtil.findType(javaProject, "parameters.Dependencies.Type4");
assertNotNull(type4);
IType type5 = EclipseJavaUtil.findType(javaProject, "parameters.Dependencies.Type4.Type5");
assertNotNull(type5);
IField f2 = type4.getField("f2");
assertNotNull(f2);

ParametedTypeFactory factory = new ParametedTypeFactory();
IParametedType ptype5 = factory.newParametedType(type5);
assertNotNull(ptype5);
System.out.println(f2.getTypeSignature());

IParametedType pf2 = factory.getParametedType(type4, f2.getTypeSignature());

try {
assertTrue(pf2.equals(ptype5));
assertTrue(ptype5.equals(pf2));
} catch (StackOverflowError e) {
fail("Stack overflow in ParametedType.equals().");
}
}

@Test
public void testIsAssignableTo() throws Exception {
IJavaProject javaProject = JavaCore.create(project);
assertNotNull(javaProject);

IType dependencies = EclipseJavaUtil.findType(javaProject, "parameters.Dependencies");
assertNotNull(dependencies);
IType type1 = EclipseJavaUtil.findType(javaProject, "parameters.Dependencies.Type1");
assertNotNull(type1);
IType type2 = EclipseJavaUtil.findType(javaProject, "parameters.Dependencies.Type2");
assertNotNull(type2);
IType type3 = EclipseJavaUtil.findType(javaProject, "parameters.Dependencies.Type3");
assertNotNull(type3);
IField f1 = dependencies.getField("f1");
assertNotNull(f1);

ParametedTypeFactory factory = new ParametedTypeFactory();
IParametedType pdependencies = factory.newParametedType(dependencies);
ParametedType ptype1 = (ParametedType)factory.newParametedType(type1);
assertNotNull(ptype1);
ParametedType ptype2 = (ParametedType)factory.newParametedType(type2);
assertNotNull(ptype2);
ParametedType ptype3 = (ParametedType)factory.newParametedType(type3);
assertNotNull(ptype3);
System.out.println(f1.getTypeSignature());

ParametedType pf1 = (ParametedType)factory.getParametedType(dependencies, f1.getTypeSignature());

try {
assertTrue(ptype1.isAssignableTo(pf1, true));
assertTrue(ptype2.isAssignableTo(pf1, true));
assertTrue(ptype3.isAssignableTo(pf1, true));
} catch (StackOverflowError e) {
fail("Stack overflow in ParametedType.isAssignableTo().");
}
}

}

0 comments on commit fcba7dd

Please sign in to comment.