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]: Spoon anonymous function cannot resolve variable type #6021

Open
jiankunking opened this issue Oct 15, 2024 · 0 comments
Open

[Bug]: Spoon anonymous function cannot resolve variable type #6021

jiankunking opened this issue Oct 15, 2024 · 0 comments
Labels

Comments

@jiankunking
Copy link

Describe the bug

Using code parsed by Spoon

@RestController
@RequestMapping("/hbdmorder")
public class HbdmOrderController {

    @Resource
    private HbdmOrderProcessingService hbdmOrderProcessingService;
	
    @PostMapping("/receive")
    public R receiveOrder(@RequestBody @Valid HbdmOrderParam hbdmOrderParam) {
        return LockUtil.receiveLock(() -> (hbdmOrderProcessingService.receiveOrder(hbdmOrderParam)), hbdmOrderParam.getInvokingSysOrderNo());
    }

}

public static R receiveLock(RFunction function, String invokingSysOrderNo) {
        return tryLockAndExecute(function, RedisConstants.ORDER_LOCK + invokingSysOrderNo);
}

@FunctionalInterface
public interface RFunction {

    R execute();
}

By parsing HbdmOrderController # receptiOrder, the following three calls can be obtained
调用的方法

But when parsing the parameter LockUtil.receiveLock, the type obtained is incorrect

type不正确

The correct type is: com. hair. hbdm. zhilian. service HbdmOrderProcessingService

But what was obtained is: com. hair. hbdm. zhilian. controller. hbdmOrderProcessingService

Analyzing Logic

 public void mavenLoad(String mavenProject, String group, String repo) {
       
        MavenLauncher launcher = new MavenLauncher(mavenProject, MavenLauncher.SOURCE_TYPE.APP_SOURCE);
        launcher.getEnvironment().setComplianceLevel(8);
        launcher.getEnvironment().setNoClasspath(true);
        launcher.buildModel();

        CtModel model = launcher.getModel();
        scan(model, group, repo);
    }
 /**
     * 扫描整个项目
     * 从controller扫描
     *
     * @param ctModel
     */
    public void scan(CtModel ctModel, String group, String repo) {
        boolean hasRequestMapping = false;

        List<CompletableFuture<Void>> futuresList = new ArrayList<>();
        // 获取所有带有Controller、RestController注解的类
        for (CtType<?> type : ctModel.getAllTypes()) {
            if (!Util.isController(type)) {
                continue;
            }
           
            log.info("class: " + type.getQualifiedName());
            if (type.isInterface()) {
                List<CtType> implementationList = listInterfaceImplElement(ctModel, type);
                if (implementationList.isEmpty()) {
                    continue;
                }
                for (CtType<?> implementation : implementationList) {
                    for (CtMethod<?> ctMethod : implementation.getAllMethods()) {
                        if (!isOverridingInterfaceMethod(type, ctMethod)) {
                            continue;
                        }
                       
                        String nextCallPkg = implementation.getPackage().toString();
                        String nextCallClazz = implementation.getSimpleName();
                        String nextCallMethod = ctMethod.getSimpleName();

                        final String traceId = UuidUtil.getUUID();
                        Node node = Node.newNode(null, null, null, null, nextCallPkg, nextCallClazz, nextCallMethod, group, repo, traceId);
                        nodeService.save(node);

                        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> findNextCalls(ctModel, nextCallPkg, nextCallClazz, nextCallMethod, paramTypesToMd5(ctMethod.getParameters()), group, repo, traceId), threadPoolTaskExecutor);
                        futuresList.add(completableFuture);
                    }
                    continue;
                }
            } else {
                for (CtMethod<?> ctMethod : type.getAllMethods()) {
                    hasRequestMapping = false;
                    for (CtAnnotation<?> methodAnnotation : ctMethod.getAnnotations()) {
                        if (!Util.isRequestMappingAnnotation(methodAnnotation)) {
                            continue;
                        }
                        hasRequestMapping = true;
                        break;
                    }
                    if (!hasRequestMapping) {
                        continue;
                    }

                  
                    String nextCallPkg = type.getPackage().toString();
                    String nextCallClazz = type.getSimpleName();
                    String nextCallMethod = ctMethod.getSimpleName();
                   
                    final String traceId = UuidUtil.getUUID();
                    Node node = Node.newNode(null, null, null, null, nextCallPkg, nextCallClazz, nextCallMethod, group, repo, traceId);
                    nodeService.save(node);

                    CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> findNextCalls(ctModel, nextCallPkg, nextCallClazz, nextCallMethod, paramTypesToMd5(ctMethod.getParameters()), group, repo, traceId), threadPoolTaskExecutor);
                    futuresList.add(completableFuture);
                }
            }

        }

        CompletableFuture[] array = futuresList.toArray(new CompletableFuture[0]);
        CompletableFuture.allOf(array).join();
    }

public void findNextCalls(CtModel ctModel, String pkg, String clazz, String method, String paramTypeMd5, String group, String repo, String traceId) {
        if (ctModel == null) {
            return;
        }

        if (StringUtil.isEmpty(pkg) || StringUtil.isEmpty(clazz) || StringUtil.isEmpty(method)) {
            return;
        }

        ctModel.getElements(new TypeFilter<CtMethod<?>>(CtMethod.class) {
            @Override
            public boolean matches(CtMethod<?> ctMethod) {
                if (!(ctMethod.getParent() instanceof CtClass)) {
                    return false;
                }
                // log.info("class: " + ctMethod.getDeclaringType().getQualifiedName() + ",method:" + ctMethod.getSimpleName());
                CtClass cz = (CtClass) ctMethod.getParent();
                String curClazz = cz.getSimpleName();
                String curPkg = null != cz.getPackage() ? cz.getPackage().getQualifiedName() : "-";
               
                if (!pkg.equals(curPkg)) {
                    return false;
                }
                if (!clazz.equals(curClazz)) {
                    return false;
                }
                if (!method.equals(ctMethod.getSimpleName())) {
                    return false;
                }
                // 方便测试时不校验参数MD5值
                if (paramTypeMd5.equals("-1")) {
                    return true;
                }
                String md5 = paramTypesToMd5(ctMethod.getParameters());
                if (!md5.equals(paramTypeMd5)) {
                    return false;
                }
                return true;

            }
        }).forEach(ctMethod -> {
            List<CtInvocation<?>> invocations = ctMethod.getElements(new TypeFilter<>(CtInvocation.class));
            String qualifiedName;
            for (CtInvocation<?> invocation : invocations) {
                if (invocation == null || invocation.getExecutable() == null || invocation.getExecutable().getDeclaringType() == null) {
                    continue;
                }
                qualifiedName = invocation.getExecutable().getDeclaringType().getQualifiedName();
                String nextCallPkg = null, nextCallClazz = null, nextCallMethod;

                if (Util.isPackage(qualifiedName)) {
                    if (!Util.isHaierPackage(qualifiedName)) {
                        continue;
                    }
                } else {
                    if (!(ctMethod.getParent() instanceof CtClass)) {
                        continue;
                    }
                }

                CtExecutableReference executable = invocation.getExecutable();
                nextCallMethod = executable.getSimpleName();

                CtPackageReference packageReference = executable.getDeclaringType().getPackage();
                if (executable.getDeclaringType().isInterface()) {
                    // 如果是接口,则查找实现类
                    List<CtType> implementationList = listInterfaceImplElement(ctModel, executable.getDeclaringType());
                    if (implementationList.isEmpty()) {
                        nextCallPkg = packageReference.getSimpleName();
                        nextCallClazz = executable.getDeclaringType().getSimpleName();
                       
                        Node node = Node.newNode(pkg, clazz, method, invocation.getPosition().toString(), nextCallPkg, nextCallClazz, nextCallMethod, group, repo, traceId);
                        nodeService.save(node);
                    } else {
                        for (CtType implementation : implementationList) {
                            nextCallPkg = implementation.getPackage().toString();
                            nextCallClazz = implementation.getSimpleName();
                          
                            nodeService.save(node);
                            // findNextCalls(ctModel, nextCallPkg, nextCallClazz, nextCallMethod, typeReferenceToMd5(executable.getParameters()), group, repo);
                            String nextTypeMd5 = typeReferenceToMd5(executable.getParameters());
                            if (pkg.equals(nextCallPkg) && clazz.equals(nextCallClazz) && method.equals(nextCallMethod) && paramTypeMd5.equals(nextTypeMd5)) {
                                // 递归调用跳过
                                continue;
                            }
                            findNextCalls(ctModel, nextCallPkg, nextCallClazz, nextCallMethod, typeReferenceToMd5(executable.getParameters()), group, repo, traceId);
                            continue;
                        }
                    }
                    continue;
                } else if (executable.getDeclaringType().isEnum()) {
                    continue;
                } else {
                    packageReference = executable.getDeclaringType().getPackage();
                    if (packageReference == null || packageReference.getSimpleName().equals("")) {
                        // 1、针对orderService.gvsLockMarketList(orderGvsLockFormList)获取不到包名及类名
                        // 回溯到类的字段上
                        // 2、无法适配lambda中的局部变量,比如下面这种
                        // 针对() -> (hbdmOrderProcessingService.receiveOrder(hbdmOrderParam)), hbdmOrderParam.getInvokingSysOrderNo()
                        // 这种情况idea的时序图插件sequence diagram也是无法识别的,具体可以readme.md
                        List<CtField<?>> ctFields = ctMethod.getParent().getElements(new TypeFilter<CtField<?>>(CtField.class));
                        if (ctFields.isEmpty()) {
                            continue;
                        }
                        for (CtField<?> ctField : ctFields) {
                            if (!ctField.getSimpleName().equals(qualifiedName)) {
                                continue;
                            }
                            nextCallPkg = ctField.getType().getPackage().getSimpleName();
                            nextCallClazz = ctField.getType().getSimpleName();
                            break;
                        }
                    } else {
                        nextCallPkg = packageReference.getSimpleName();
                        nextCallClazz = executable.getDeclaringType().getSimpleName();
                    }

                 
                    Node node = Node.newNode(pkg, clazz, method, invocation.getPosition().toString(), nextCallPkg, nextCallClazz, nextCallMethod, group, repo, traceId);
                    nodeService.save(node);

                    String nextTypeMd5 = typeReferenceToMd5(executable.getParameters());
                    if (pkg.equals(nextCallPkg) && clazz.equals(nextCallClazz) && method.equals(nextCallMethod) && paramTypeMd5.equals(nextTypeMd5)) {
                        // 递归调用跳过
                        continue;
                    }
                    findNextCalls(ctModel, nextCallPkg, nextCallClazz, nextCallMethod, typeReferenceToMd5(executable.getParameters()), group, repo, traceId);
                    continue;
                }
            }
        });
    }

Source code you are trying to analyze/transform

No response

Source code for your Spoon processing

No response

Actual output

No response

Expected output

No response

Spoon Version

11.1.0

JVM Version

21.0.1

What operating system are you using?

Windows 10 专业版 21H2

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

3 participants
@jiankunking and others