Skip to content

Commit

Permalink
Add visitor to easily determine, if a named variable will escape its …
Browse files Browse the repository at this point in the history
…defining scope or which variables will escape its defining scope
  • Loading branch information
MBoegers committed Nov 8, 2024
1 parent 1088ccc commit 1c83352
Show file tree
Hide file tree
Showing 3 changed files with 529 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.search;

import org.openrewrite.ExecutionContext;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.marker.SearchResult;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Identify local variables that escape there defining scope.
*
* @see FindVariableEscapeLocations
*/
public class FindEscapingVariables extends JavaIsoVisitor<ExecutionContext> {
/**
* Finds named local variables from the given subtree that will escape their defining scope
*/
public static Set<J.VariableDeclarations.NamedVariable> find(J subtree) {
return TreeVisitor.collect(new FindEscapingVariables(), subtree, new HashSet<>())
.stream()
.filter(J.VariableDeclarations.NamedVariable.class::isInstance)
.map(J.VariableDeclarations.NamedVariable.class::cast)
.collect(Collectors.toSet());
}

/**
* Determine if a local named variable from the given subtree will escape its defining scope
*/
public static boolean willLeadFrom(J.VariableDeclarations.NamedVariable variable, J subtree) {
return find(subtree).contains(variable);
}

@Override
public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext executionContext) {
variable = super.visitVariable(variable, executionContext);

if (variable.isField(getCursor())) {
return variable;
}

J.Block definingScope = getCursor().firstEnclosing(J.Block.class);
if (definingScope != null && variable.getName().getFieldType() != null) {
boolean willLeak = FindVariableEscapeLocations.findLeakingVars(definingScope).stream()
.map(J.Identifier::getFieldType)
.anyMatch(variable.getName().getFieldType()::equals);
if (willLeak) {
variable = SearchResult.found(variable);
}
}

return variable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.openrewrite.java.tree.Statement;
import org.openrewrite.marker.SearchResult;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -112,15 +113,9 @@ public static Set<J.Identifier> findLeakingVars(J subtree) {
Function<? super Tree, Set<J.Identifier>> extractIdentifiers = t -> {
Set<J.Identifier> identifiers = new HashSet<>();
if (t instanceof J.Return) {
Expression expr = ((J.Return) t).getExpression();
if (expr instanceof J.Identifier) {
identifiers.add((J.Identifier) expr);
}
identifiers.addAll(extractIdentifiers(((J.Return) t).getExpression()));
} else if (t instanceof J.Assignment) {
Expression expr = ((J.Assignment) t).getAssignment();
if (expr instanceof J.Identifier) {
identifiers.add((J.Identifier) expr);
}
identifiers.addAll(extractIdentifiers(((J.Assignment) t).getAssignment()));
} else if (t instanceof J.NewClass) {
identifiers.addAll(findLocalArguments(((J.NewClass) t).getArguments()));
} else if(t instanceof J.MethodInvocation) {
Expand All @@ -141,7 +136,7 @@ public static Set<J.Identifier> findLeakingVars(J subtree) {
* Extracts Set of Identifiers from J.Identifiers and Ternary operators.
* These two potentially participate in very case but will not lead to escaping themselves.
*/
private static Set<J.Identifier> extractIdentifiers(Expression assignment) {
private static Set<J.Identifier> extractIdentifiers(@Nullable Expression assignment) {
Set<J.Identifier> identifiers = new HashSet<>();
// fast return if already an J.Identifier
if (assignment instanceof J.Identifier) {
Expand Down
Loading

0 comments on commit 1c83352

Please sign in to comment.