Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JBIDE-21036 Infinite cycle in ParametedType.isAssignableTo() #464

Merged
merged 1 commit into from
Nov 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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().");
}
}

}