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

[Bug]: StackOverflowError occurred in CtExecutableReference.getExecutableDeclaration #5584

Open
sretake opened this issue Dec 14, 2023 · 5 comments
Labels

Comments

@sretake
Copy link
Contributor

sretake commented Dec 14, 2023

Describe the bug

StackOverflowError occurred in CtExecutableReference.getExecutableDeclaration

Source code you are trying to analyze/transform

import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;

class A {
    public void checkDataValidDimension(String versionCode, List<? extends FcBasicDataVO> fcBasicDataList,
        BiFunction<FcBasicDataVO, FcBasicDataFullVo, Boolean> isMatchFunc, Function<FcBasicDataVO, String> msgFunc,
        BiConsumer<FcBasicDataVO, FcBasicDataFullVo> matchedFunc) {

        // dimensionVo List
        List<FcBasicDataFullVo> dimensionVoList = getFcDimensionListByVersionCode(versionCode);
        fcBasicDataList.stream().forEach(checkVo -> {
            for (FcBasicDataFullVo dimensionVo : dimensionVoList) {
                if (isMatchFunc.apply(checkVo, dimensionVo)) {
                }
            }
        });
    }
}

Source code for your Spoon processing

Launcher launcher = new Launcher();
        launcher.getEnvironment().setComplianceLevel(11);
        launcher.getEnvironment().setAutoImports(true);
        launcher.getEnvironment().setIgnoreDuplicateDeclarations(true);
        launcher.addInputResource(filteringFolder);
        launcher.run();
        List<CtExecutableReference> elements = model.getElements(
            new spoon.reflect.visitor.filter.TypeFilter<>(CtExecutableReference.class));
        for (CtExecutableReference element : elements) {
            element.getExecutableDeclaration();
        }

Actual output

java.lang.StackOverflowError
	at spoon.support.util.internal.ElementNameMap.get(ElementNameMap.java:169)
	at spoon.support.reflect.declaration.CtPackageImpl.getPackage(CtPackageImpl.java:130)
	at spoon.reflect.factory.PackageFactory.getPackageFromModule(PackageFactory.java:216)
	at spoon.reflect.factory.PackageFactory.get(PackageFactory.java:176)
	at spoon.reflect.factory.TypeFactory.get(TypeFactory.java:434)
	at spoon.reflect.factory.TypeFactory.get(TypeFactory.java:562)
	at spoon.support.reflect.reference.CtTypeReferenceImpl.getTypeDeclaration(CtTypeReferenceImpl.java:245)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)
	at spoon.support.reflect.declaration.CtTypeImpl.isSameParameter(CtTypeImpl.java:767)
	at spoon.support.reflect.declaration.CtTypeImpl.hasSameParameters(CtTypeImpl.java:759)
	at spoon.support.reflect.declaration.CtTypeImpl.getMethod(CtTypeImpl.java:733)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getCtExecutable(CtExecutableReferenceImpl.java:121)
	at spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration(CtExecutableReferenceImpl.java:113)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getDeclaration(CtTypeParameterReferenceImpl.java:134)
	at spoon.support.reflect.reference.CtTypeParameterReferenceImpl.getTypeErasure(CtTypeParameterReferenceImpl.java:174)

Expected output

.

Spoon Version

10.4.3-beta-10

JVM Version

11

What operating system are you using?

windows 11

@sretake sretake added the bug label Dec 14, 2023
@sretake
Copy link
Contributor Author

sretake commented Dec 29, 2023

When class C is not in the current project, stack overflow will occur in the following test example

        @Test()
	void foo(){
		Launcher launcher = new Launcher();
		launcher.getEnvironment().setComplianceLevel(11);
		launcher.getEnvironment().setAutoImports(true);
		launcher.addInputResource(new VirtualFile("" +
			"import com.C;\n" +
			"import java.util.List;\n" +
			"import java.util.function.BiFunction;\n" +
			"\n" +
			"public class A {\n" +
			"    public void test( List<? extends C> list, BiFunction<C, B, Boolean> func) {\n" +
			"        list.stream().forEach(c -> {\n" +
			"            func.apply(c, null);\n" +
			"        });\n" +
			"    }\n" +
			"}\n" +
			"class B{ }" +
			""));
		CtModel ctModel = launcher.buildModel();
		List<CtExecutableReference> elements = ctModel.getElements(new TypeFilter<>(CtExecutableReference.class));
		for (CtExecutableReference element : elements) {
			try{
				element.getExecutableDeclaration();
			}catch(StackOverflowError e){
			    e.printStackTrace();
			}
		}
	}

@sretake
Copy link
Contributor Author

sretake commented Dec 29, 2023

When class C is in the current project, the following test cases are normal

        @Test()
	void foo(){
		Launcher launcher = new Launcher();
		launcher.getEnvironment().setComplianceLevel(11);
		launcher.getEnvironment().setAutoImports(true);
		launcher.addInputResource(new VirtualFile("" +
			"import java.util.List;\n" +
			"import java.util.function.BiFunction;\n" +
			"\n" +
			"public class A {\n" +
			"    public void test( List<? extends C> list, BiFunction<C, B, Boolean> func) {\n" +
			"        list.stream().forEach(c -> {\n" +
			"            func.apply(c, null);\n" +
			"        });\n" +
			"    }\n" +
			"}\n" +
			"class B{ }class C{}" +
			""));
		CtModel ctModel = launcher.buildModel();
		List<CtExecutableReference> elements = ctModel.getElements(new TypeFilter<>(CtExecutableReference.class));
		for (CtExecutableReference element : elements) {
			try{
				element.getExecutableDeclaration();
			}catch(StackOverflowError e){
			    e.printStackTrace();
			}
		}
	}

@sretake
Copy link
Contributor Author

sretake commented Jan 8, 2024

Can anyone answer me if it's actually a bug
@alcides

@I-Al-Istannen
Copy link
Collaborator

I-Al-Istannen commented Jan 8, 2024

I mean, it is kind of a bug but not really. It only happens in noclasspath mode, because in there JDT's type inference gives up:
grafik
As JDT is not able to resolve this to the apply(Object, Object) method in the class, Spoon would need to make that translation on a best-effort basis. Sadly, during erasure computation for the parameter spoon enters an infinite loop. I am sure spoon could be more clever here, but it still won't really be able to answer that query correctly with the types provided by JDT.

The question is whether we want to try and make Spoon a bit smarter here and let it return from the call -- possibly returning garbage information. We could also make it return null, I guess, but I am not sure how straightforward the required changes in spoon would be (if at all possible to detect).

@sretake
Copy link
Contributor Author

sretake commented Jan 9, 2024

thanks, i suggest you can return null or throw SpoonException instead of StackOverflowError , because it will cause my program to crash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants