|
44 | 44 | import spoon.reflect.code.CtForEach; |
45 | 45 | import spoon.reflect.code.CtIf; |
46 | 46 | import spoon.reflect.code.CtInvocation; |
| 47 | +import spoon.reflect.code.CtLambda; |
47 | 48 | import spoon.reflect.code.CtLiteral; |
48 | 49 | import spoon.reflect.code.CtLocalVariable; |
49 | 50 | import spoon.reflect.code.CtLoop; |
|
52 | 53 | import spoon.reflect.code.CtOperatorAssignment; |
53 | 54 | import spoon.reflect.code.CtReturn; |
54 | 55 | import spoon.reflect.code.CtStatement; |
| 56 | +import spoon.reflect.code.CtSuperAccess; |
55 | 57 | import spoon.reflect.code.CtThisAccess; |
56 | 58 | import spoon.reflect.code.CtThrow; |
57 | 59 | import spoon.reflect.code.CtTry; |
@@ -400,6 +402,87 @@ public <R> void visitCtReturn(CtReturn<R> ret) { |
400 | 402 | mfc.getReturnRefinements(ret); |
401 | 403 | } |
402 | 404 |
|
| 405 | + @Override |
| 406 | + public <T> void visitCtLambda(CtLambda<T> lambda) { |
| 407 | + Context.DeferredScope scope = context.enterDeferredScope(); |
| 408 | + List<RefinedVariable> pathVariables = vcChecker.getPathVariables(); |
| 409 | + try { |
| 410 | + // Only facts about immutable primitive captures survive until an unknown later invocation. |
| 411 | + Set<String> immutableNames = immutableLambdaCaptureNames(lambda); |
| 412 | + vcChecker |
| 413 | + .replacePathVariables(pathVariables.stream() |
| 414 | + .filter(path -> path.getRefinement().getVariableNames().stream() |
| 415 | + .allMatch(name -> name.equals(path.getName()) || immutableNames.contains(name))) |
| 416 | + .toList()); |
| 417 | + havocLambdaCaptures(lambda); |
| 418 | + for (CtParameter<?> parameter : lambda.getParameters()) { |
| 419 | + Predicate declared = getRefinementFromAnnotation(parameter).orElseGet(Predicate::new) |
| 420 | + .substituteVariable(Keys.WILDCARD, parameter.getSimpleName()); |
| 421 | + context.addVarToContext(parameter.getSimpleName(), parameter.getType(), declared, parameter); |
| 422 | + } |
| 423 | + super.visitCtLambda(lambda); |
| 424 | + } catch (LJError e) { |
| 425 | + diagnostics.add(e); |
| 426 | + } finally { |
| 427 | + context.exitDeferredScope(scope); |
| 428 | + vcChecker.replacePathVariables(pathVariables); |
| 429 | + } |
| 430 | + } |
| 431 | + |
| 432 | + private Set<String> immutableLambdaCaptureNames(CtLambda<?> lambda) { |
| 433 | + Set<String> names = new LinkedHashSet<>(); |
| 434 | + for (CtVariableAccess<?> access : lambda |
| 435 | + .getElements(new TypeFilter<CtVariableAccess<?>>(CtVariableAccess.class))) { |
| 436 | + CtVariable<?> declaration = access.getVariable().getDeclaration(); |
| 437 | + if (access instanceof CtFieldAccess<?> || access.getType() == null || !access.getType().isPrimitive() |
| 438 | + || declaration == null || declaration.hasParent(lambda)) |
| 439 | + continue; |
| 440 | + CtExecutable<?> executable = declaration instanceof CtParameter<?> parameter |
| 441 | + ? parameter.getParent(CtExecutable.class) : declaration.getParent(CtExecutable.class); |
| 442 | + if (executable == null || executable.getElements(new TypeFilter<>(CtVariableWrite.class)).stream() |
| 443 | + .anyMatch(write -> write.getVariable().getDeclaration() == declaration)) |
| 444 | + continue; |
| 445 | + names.add(access.getVariable().getSimpleName()); |
| 446 | + } |
| 447 | + for (RefinedVariable rv : context.getCtxInstanceVars()) |
| 448 | + if (rv instanceof VariableInstance instance |
| 449 | + && instance.getParent().map(parent -> names.contains(parent.getName())).orElse(false)) |
| 450 | + names.add(instance.getName()); |
| 451 | + return names; |
| 452 | + } |
| 453 | + |
| 454 | + private void havocLambdaCaptures(CtLambda<?> lambda) { |
| 455 | + Set<String> names = new LinkedHashSet<>(); |
| 456 | + for (CtVariableAccess<?> access : lambda |
| 457 | + .getElements(new TypeFilter<CtVariableAccess<?>>(CtVariableAccess.class))) { |
| 458 | + CtVariable<?> declaration = access.getVariable().getDeclaration(); |
| 459 | + if (declaration != null && declaration.hasParent(lambda)) |
| 460 | + continue; |
| 461 | + if (access instanceof CtFieldAccess<?> field) { |
| 462 | + names.add(Utils.qualifyFieldName(field.getVariable())); |
| 463 | + } else if (access instanceof CtSuperAccess<?> parent) { |
| 464 | + if (parent.getTarget() == null || parent.getTarget().isImplicit()) |
| 465 | + names.add(Keys.THIS); |
| 466 | + } else if (access.getType() != null && !access.getType().isPrimitive()) { |
| 467 | + names.add(access.getVariable().getSimpleName()); |
| 468 | + } |
| 469 | + } |
| 470 | + for (CtThisAccess<?> self : lambda.getElements(new TypeFilter<CtThisAccess<?>>(CtThisAccess.class))) { |
| 471 | + CtType<?> owner = self.getParent(CtType.class); |
| 472 | + if (owner != null && self.getType() != null |
| 473 | + && self.getType().getQualifiedName().equals(owner.getQualifiedName())) |
| 474 | + names.add(Keys.THIS); |
| 475 | + } |
| 476 | + for (String name : names) { |
| 477 | + if (!(context.getVariableByName(name)instanceof Variable variable)) |
| 478 | + continue; |
| 479 | + String instanceName = String.format(Formats.INSTANCE, name, context.getCounter()); |
| 480 | + Predicate declared = variable.getMainRefinement().substituteVariable(name, instanceName); |
| 481 | + context.addInstanceToContext(instanceName, variable.getType(), declared, lambda); |
| 482 | + context.addRefinementInstanceToVariable(name, instanceName); |
| 483 | + } |
| 484 | + } |
| 485 | + |
403 | 486 | @Override |
404 | 487 | public void visitCtIf(CtIf ifElement) { |
405 | 488 | CtExpression<Boolean> exp = ifElement.getCondition(); |
|
0 commit comments