Skip to content

Commit

Permalink
perf: improve tree transformation stage performance
Browse files Browse the repository at this point in the history
  • Loading branch information
ishche committed Jan 8, 2025
1 parent 4baefd0 commit a0d978b
Show file tree
Hide file tree
Showing 28 changed files with 415 additions and 489 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,8 @@ public Location mapLocation(Range range) {
return new Location(uri, range);
}
for (Mapper mapper : mappers) {
Location result = mapper.apply(startCharacter, endCharacter);
if (result != null) {
return result;
if (mapper.canApply(startCharacter, endCharacter)) {
return mapper.apply(startCharacter, endCharacter);
}
}
throw new RuntimeException("Cannot find original position");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,24 @@
* Handle insert mapping situations
*/
class InsertMapper implements Mapper {
@Override
public boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter) {
if (startCharacter == null || endCharacter == null) {
return false;
}
if (startCharacter.getOriginalPosition() == null) {
return false;
}
return !startCharacter.getUri().equals(endCharacter.getUri());
}

@Override
public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) {
if (startCharacter != null && !startCharacter.getUri().equals(endCharacter.getUri()) && startCharacter.getOriginalPosition() != null) {
Map<String, Location> ilm = endCharacter.getInitialLocationMap();
Location location = ilm != null ? ilm.get(startCharacter.getUri()) : null;
if (location != null) {
return new Location(startCharacter.getUri(), new Range(startCharacter.getOriginalPosition(), location.getRange().getEnd()));
}
return new Location(startCharacter.getUri(), new Range(startCharacter.getOriginalPosition(), startCharacter.getOriginalPosition()));
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
* Provides mapping functionality
*/
interface Mapper {
boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter);
Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,29 @@
*/
class ReplaceMapper implements Mapper {

@Override
public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) {
if (!startCharacter.getUri().equals(endCharacter.getUri())) {
endCharacter = startCharacter;
@Override
public boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter) {
if (!startCharacter.getUri().equals(endCharacter.getUri())) {
endCharacter = startCharacter;
}
return startCharacter.getOriginalPosition() == null || endCharacter.getOriginalPosition() == null;
}

if (startCharacter.getOriginalPosition() == null || endCharacter.getOriginalPosition() == null) {
if (startCharacter.getInstantLocation() == null && endCharacter.getInstantLocation() == null) {
throw new RuntimeException("Cannot find original position");
}
if (endCharacter.getOriginalPosition() != null) {
Range calculatedRange = new Range(startCharacter.getInstantLocation().getRange().getStart(), endCharacter.getOriginalPosition());
return new Location(startCharacter.getUri(), calculatedRange);
} else if (startCharacter.getOriginalPosition() != null) {
Range calculatedRange = new Range(startCharacter.getOriginalPosition(), endCharacter.getInstantLocation().getRange().getEnd());
return new Location(startCharacter.getUri(), calculatedRange);
}
return Optional.ofNullable(startCharacter.getInstantLocation()).orElse(endCharacter.getInstantLocation());
@Override
public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) {
if (!startCharacter.getUri().equals(endCharacter.getUri())) {
endCharacter = startCharacter;
}
if (startCharacter.getInstantLocation() == null && endCharacter.getInstantLocation() == null) {
throw new RuntimeException("Cannot find original position");
}
if (endCharacter.getOriginalPosition() != null) {
Range calculatedRange = new Range(startCharacter.getInstantLocation().getRange().getStart(), endCharacter.getOriginalPosition());
return new Location(startCharacter.getUri(), calculatedRange);
} else if (startCharacter.getOriginalPosition() != null) {
Range calculatedRange = new Range(startCharacter.getOriginalPosition(), endCharacter.getInstantLocation().getRange().getEnd());
return new Location(startCharacter.getUri(), calculatedRange);
}
return Optional.ofNullable(startCharacter.getInstantLocation()).orElse(endCharacter.getInstantLocation());
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
*/
class StraightforwardMapper implements Mapper {

@Override
public boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter) {
return true;
}

@Override
public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) {
Position startPosition = new Position(startCharacter.getOriginalPosition().getLine(), startCharacter.getOriginalPosition().getCharacter());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,28 @@
*/
package org.eclipse.lsp.cobol.common.model.tree;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.eclipse.lsp.cobol.common.model.Locality;
import org.eclipse.lsp.cobol.common.model.NodeType;

import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
import java.util.stream.Stream;

/** The class represents a Node in source structure tree. */
@ToString
@Getter
@EqualsAndHashCode
public abstract class Node {
@Setter protected Locality locality;
@Setter protected String text;
private final NodeType nodeType;
private final String dialect;

@EqualsAndHashCode.Exclude private final List<Node> children = new CopyOnWriteArrayList<>();
@EqualsAndHashCode.Exclude @ToString.Exclude @Setter private transient Node parent;
private final List<Node> children = new CopyOnWriteArrayList<>();
@ToString.Exclude @Setter private transient Node parent;

protected Node(Locality location, NodeType nodeType, String dialect) {
this.locality = location;
Expand Down Expand Up @@ -103,15 +100,70 @@ public Stream<Node> getDepthFirstStream() {
return Stream.concat(Stream.of(this), children.stream().flatMap(Node::getDepthFirstStream));
}

/**
* Get a list with all nested children starting with this instance.
*
* @return a list with all underline children.
*/
public List<Node> getDepthFirstList(Predicate<Node> nodePredicate) {
ArrayList<Node> result = new ArrayList<>();
if (nodePredicate.test(this)) {
result.add(this);
}
LinkedList<Node> queue = new LinkedList<>(getChildren());
while (!queue.isEmpty()) {
Node node = queue.remove();
if (nodePredicate.test(node)) {
result.add(node);
}
for (Node child : node.getChildren()) {
queue.addFirst(child);
}
}
return result;
}

/**
* Get the first node that meet predicate condition.
*
* @return a node if there is any or null.
*/
public Node getDepthFirstFirstNode(Predicate<Node> nodePredicate) {
if (nodePredicate.test(this)) {
return this;
}
LinkedList<Node> queue = new LinkedList<>();
for (Node child: getChildren()) {
if (nodePredicate.test(child)) {
return child;
}
queue.add(child);
}

while (!queue.isEmpty()) {
Node node = queue.remove();
for (Node child : node.getChildren()) {
if (nodePredicate.test(child)) {
return child;
}
queue.addFirst(child);
}
}
return null;
}

/**
* Get nearest parent with specified type.
*
* @param type required node type.
* @return an optional with requested nearest node.
*/
public Optional<Node> getNearestParentByType(NodeType type) {
return Optional.ofNullable(parent)
.flatMap(it -> (it.nodeType == type) ? Optional.of(it) : it.getNearestParentByType(type));
Node result = parent;
while (result != null && !type.equals(result.getNodeType())) {
result = result.getParent();
}
return Optional.ofNullable(result);
}
/**
* Get nearest parent using predicate.
Expand All @@ -132,4 +184,22 @@ public Optional<Node> getNearestParent(Predicate<Node> predicate) {
public Optional<ProgramNode> getProgram() {
return getNearestParentByType(NodeType.PROGRAM).map(ProgramNode.class::cast);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

Node node = (Node) o;
return Objects.equals(locality, node.locality) && Objects.equals(text, node.text) && nodeType == node.nodeType && Objects.equals(dialect, node.dialect);
}

@Override
public int hashCode() {
int result = Objects.hashCode(locality);
result = 31 * result + Objects.hashCode(text);
result = 31 * result + Objects.hashCode(nodeType);
result = 31 * result + Objects.hashCode(dialect);
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
@Getter
@ToString(callSuper = true)
@SuppressWarnings("squid:S107")
@EqualsAndHashCode(callSuper = true)
//@EqualsAndHashCode(callSuper = true)
public final class VariableDefinitionNode extends Node {
private static final String SPACES_AFTER_NEWLINE_REGEX = "[\\r\\n|\\r|\\n]\\s+";
private final int level;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.eclipse.lsp4j.Range;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

import static java.util.stream.Collectors.toList;
Expand All @@ -39,16 +40,14 @@
import static org.eclipse.lsp.cobol.common.model.NodeType.VARIABLE_DEFINITION_NAME;

/** The abstract class for all variable definitions. */
@Getter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public abstract class VariableNode extends Node implements DefinedAndUsedStructure {

public static final String PREFIX = " ";
private final VariableType variableType;
private final String name;
@Setter private boolean global;
@EqualsAndHashCode.Exclude private final List<Location> usages = new ArrayList<>();
@Getter private final VariableType variableType;
@Getter private final String name;
@Getter @Setter private boolean global;
@EqualsAndHashCode.Exclude private final LinkedHashSet<Location> usages = new LinkedHashSet<>();

protected VariableNode(
Locality location, String name, VariableType variableType, boolean global) {
Expand Down Expand Up @@ -189,6 +188,11 @@ private String getDisplayStringWithConditionals() {
return String.join("\n", result);
}

@Override
public List<Location> getUsages() {
return new ArrayList<>(usages);
}

private static String prepend(String prefix, String text) {
return prefix + text.replace("\n", "\n" + prefix);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ public VariableUsageNode(

@Override
public List<Location> getDefinitions() {
return definitions.stream().map(VariableNode::getLocality).map(Locality::toLocation).collect(Collectors.toList());
List<Location> result = new ArrayList<>(definitions.size());
for (VariableNode definition : definitions) {
result.add(definition.getLocality().toLocation());
}
return result;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.eclipse.lsp.cobol.common.model.tree.CodeBlockDefinitionNode;
import org.eclipse.lsp.cobol.common.model.tree.ProgramNode;
import org.eclipse.lsp.cobol.common.model.tree.variable.VariableNode;
import org.eclipse.lsp4j.Range;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;

import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -40,6 +42,12 @@ public class SymbolTable {
* @return string value of a generated key
*/
public static String generateKey(ProgramNode program) {
return program.getProgramName() + "%" + program.getLocality().getUri() + "%" + program.getLocality().getRange();
Range range = program.getLocality().getRange();
String rangeString = "["
+ range.getStart().getLine() + ", " + range.getStart().getCharacter()
+ "-"
+ range.getEnd().getLine() + ", " + range.getEnd().getCharacter()
+ "]";
return program.getProgramName() + "%" + program.getLocality().getUri() + "%" + rangeString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,13 @@
*/
@Slf4j
public class AstProcessor {

/**
* The entry point to AST processing
*
* @param analysisConfig
* @param ctx processing context
* @param analysisContext
* @param rootNode the root node of AST
* @param analysisConfig analysis config
* @param ctx processing context
* @param analysisContext analysis context
* @param rootNode the root node of AST
* @return a list of errors
*/
public List<SyntaxError> processSyntaxTree(AnalysisConfig analysisConfig, ProcessingContext ctx, AnalysisContext analysisContext, Node rootNode) {
Expand All @@ -58,7 +57,7 @@ public List<SyntaxError> processSyntaxTree(AnalysisConfig analysisConfig, Proces
return ctx.getErrors();
}

/**
/**
* Process tree node and its children after tree construction.
*
* @param phase processing phase
Expand All @@ -74,20 +73,24 @@ public void process(ProcessingPhase phase, Node node, ProcessingContext ctx) {
/**
* Process tree node and its children after tree construction.
*
* @param processor list of available processors
* @param processors list of available processors
* @param node a node to process
* @param ctx processing context
*/
private void process(Map<Class<? extends Node>, List<Processor<? extends Node>>> processors,
Node node, ProcessingContext ctx) {
ThreadInterruptionUtil.checkThreadInterrupted();
final Class<? extends Node> nodeClass = node.getClass();
processors.forEach((key, value) -> {
if (!key.isAssignableFrom(nodeClass))
return;
for (Processor<? extends Node> p : value)
((Processor<Node>) p).accept(node, ctx);
});
node.getChildren().forEach(n -> process(processors, n, ctx));
for (Map.Entry<Class<? extends Node>, List<Processor<? extends Node>>> entry : processors.entrySet()) {
Class<? extends Node> key = entry.getKey();
List<Processor<? extends Node>> value = entry.getValue();
if (!key.isAssignableFrom(nodeClass))
continue;
for (Processor<? extends Node> p : value)
((Processor<Node>) p).accept(node, ctx);
}
for (Node n : node.getChildren()) {
process(processors, n, ctx);
}
}
}
Loading

0 comments on commit a0d978b

Please sign in to comment.