Skip to content

Commit

Permalink
feat(objectionary#752): add DataFlow and MaxStackFlow classes as firs…
Browse files Browse the repository at this point in the history
…t refactoring examples
  • Loading branch information
volodya-lombrozo committed Oct 11, 2024
1 parent 12ea088 commit b35719e
Show file tree
Hide file tree
Showing 4 changed files with 359 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,14 @@ DirectivesMethod directives() {
* @return Max stack.
*/
private int computeStack() {
return new MaxStack(
// return new MaxStack(
// this.instructions,
// this.tryblocks.stream()
// .filter(BytecodeTryCatchBlock.class::isInstance)
// .map(BytecodeTryCatchBlock.class::cast)
// .collect(Collectors.toList())
// ).value();
return new MaxStackFlow(
this.instructions,
this.tryblocks.stream()
.filter(BytecodeTryCatchBlock.class::isInstance)
Expand Down
156 changes: 156 additions & 0 deletions src/main/java/org/eolang/jeo/representation/bytecode/DataFlow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2024 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.eolang.jeo.representation.bytecode;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.objectweb.asm.Label;

public final class DataFlow<T extends DataFlow.Something<T>> {
private final InstructionsFlow instructions;

private final List<BytecodeTryCatchBlock> blocks;

private final T initial;

private final Function<BytecodeInstruction, T> generate;

public DataFlow(
final InstructionsFlow instructions,
final List<BytecodeTryCatchBlock> blocks,
final T initial,
Function<BytecodeInstruction, T> generator
) {
this.instructions = instructions;
this.blocks = blocks;
this.initial = initial;
this.generate = generator;
}

// this.blocks.stream()
// .map(BytecodeTryCatchBlock.class::cast)
// .map(BytecodeTryCatchBlock::handlerLabel)
// .map(this.instructions::index)
// .peek(ind -> visited.put(ind, 1))
// .forEach(worklist::add);


public T max() {
final Map<Integer, T> visited = new HashMap<>(0);
final Map<Integer, T> worklist = new HashMap<>(0);
worklist.put(0, this.initial);
final int total = this.instructions.size();
T current;
while (!worklist.isEmpty()) {
final Map.Entry<Integer, T> curr = worklist.entrySet()
.stream()
.findFirst()
.orElseThrow(() -> new IllegalStateException("Cannot find first worklist element"));
int index = curr.getKey();
current = curr.getValue();
worklist.remove(index);
if (visited.get(index) != null) {
if (visited.get(index).compareTo(current) >= 0) {
continue;
}
}
while (index < total) {
final BytecodeEntry entry = this.instructions.get(index);
if (entry instanceof BytecodeInstruction) {
final BytecodeInstruction instruction = BytecodeInstruction.class.cast(entry);
current = current.add(this.generate.apply(instruction));
final T updated = current;
if (instruction.isSwitch()) {
final List<Label> offsets = instruction.offsets();
for (final Label offset : offsets) {
final int target = this.instructions.index(offset);
worklist.put(target, updated);
}
visited.put(index, updated);
break;
} else if (instruction.isBranch()) {
final Label label = instruction.jump();
final int jump = this.instructions.index(label);
worklist.put(jump, updated);
final int next = index + 1;
worklist.put(next, updated);
visited.put(index, updated);
break;
} else if (instruction.isJump()) {
final Label label = instruction.jump();
final int jump = this.instructions.index(label);
worklist.put(jump, updated);
visited.put(index, updated);
break;
} else if (instruction.isReturn()) {
visited.put(index, updated);
break;
}
this.suitableBlocks(index)
.forEach(ind -> worklist.put(ind, updated.initBlock())
);
visited.putIfAbsent(index, updated);
visited.computeIfPresent(index, (k, v) -> this.max(v, updated));
} else {
visited.put(index, current);
}
++index;
}
}
return visited.values()
.stream()
.max(T::compareTo)
.orElseThrow(() -> new IllegalStateException("Cannot find max value"));
}

/**
* Which try-catch-blocks cover the instruction.
* @param instruction Instruction index.
* @return List of block indexes.
*/
private List<Integer> suitableBlocks(final int instruction) {
return this.blocks.stream()
.map(BytecodeTryCatchBlock.class::cast)
.filter(block -> this.instructions.index(block.startLabel()) <= instruction)
.filter(block -> this.instructions.index(block.endLabel()) >= instruction)
.map(block -> this.instructions.index(block.handlerLabel()))
.collect(Collectors.toList());
}

private T max(final T a, final T b) {
return a.compareTo(b) > 0 ? a : b;
}

interface Something<T> extends Comparable<T> {

T add(T other);

T initBlock();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ private MaxStack(
@SuppressWarnings("PMD.CognitiveComplexity")
public int value() {
int max = 0;
final Deque<Integer> worklist = new ArrayDeque<>(0);
final int length = this.instructions.size();
final Deque<Integer> worklist = new ArrayDeque<>(0);
worklist.add(0);
final Map<Integer, Integer> visited = new TreeMap<>();
this.blocks.stream()
Expand Down
194 changes: 194 additions & 0 deletions src/main/java/org/eolang/jeo/representation/bytecode/MaxStackFlow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2024 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.eolang.jeo.representation.bytecode;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
import lombok.ToString;
import org.objectweb.asm.Label;

public final class MaxStackFlow {

/**
* Method instructions.
*/
private final InstructionsFlow instructions;

/**
* Try-catch blocks.
*/
private final List<BytecodeTryCatchBlock> blocks;


/**
* Constructor.
* @param instructions Instructions.
* @param catches Try-catch blocks.
*/
MaxStackFlow(
final List<? extends BytecodeEntry> instructions,
final List<BytecodeTryCatchBlock> catches
) {
this(new InstructionsFlow(instructions), catches);
}

/**
* Compute the maximum stack size.
* @param instructions Instructions.
* @param catches Try-catch blocks.
*/
private MaxStackFlow(
final InstructionsFlow instructions,
final List<BytecodeTryCatchBlock> catches
) {
this.instructions = instructions;
this.blocks = catches;
}

/**
* Compute the maximum stack size.
* @return Maximum stack size.
*/
@SuppressWarnings("PMD.CognitiveComplexity")
public int value() {
return new DataFlow<>(
this.instructions,
this.blocks,
new Stack(0),
Stack::new
).max().value;

// int max = 0;
// final int length = this.instructions.size();
// final Deque<Integer> worklist = new ArrayDeque<>(0);
// worklist.add(0);
// final Map<Integer, Integer> visited = new TreeMap<>();
// this.blocks.stream()
// .map(BytecodeTryCatchBlock.class::cast)
// .map(BytecodeTryCatchBlock::handlerLabel)
// .map(this.instructions::index)
// .peek(ind -> visited.put(ind, 1))
// .forEach(worklist::add);
// while (!worklist.isEmpty()) {
// int current = worklist.pop();
// int stack = visited.get(current) == null ? 0 : visited.get(current);
// while (current < length) {
// final BytecodeEntry entry = this.instructions.get(current);
// stack += entry.impact();
// max = Math.max(max, stack);
// final int fstack = stack;
// visited.compute(
// current, (k, v) -> v == null ? fstack : Math.max(v, fstack)
// );
// if (entry instanceof BytecodeInstruction) {
// final BytecodeInstruction var = BytecodeInstruction.class.cast(entry);
// if (var.isSwitch()) {
// final List<Label> offsets = var.offsets();
// for (final Label offset : offsets) {
// final int target = this.instructions.index(offset);
// if (visited.get(target) == null
// || visited.get(target) < stack
// ) {
// visited.put(target, stack);
// worklist.add(target);
// }
// }
// break;
// } else if (var.isBranch()) {
// final Label label = var.jump();
// final int jump = this.instructions.index(label);
// if (visited.get(jump) == null
// || visited.get(jump) < stack
// ) {
// visited.put(jump, stack);
// worklist.add(jump);
// }
// final int next = current + 1;
// if (visited.get(next) == null
// || visited.get(next) < stack
// ) {
// visited.put(next, stack);
// worklist.add(next);
// }
// break;
// } else if (var.isJump()) {
// final Label label = var.jump();
// final int jump = this.instructions.index(label);
// if (visited.get(jump) == null
// || visited.get(jump) < stack
// ) {
// visited.put(jump, stack);
// worklist.add(jump);
// }
// break;
// } else if (var.isReturn()) {
// break;
// }
// }
// ++current;
// }
// }
// return max;
}

@ToString
private static class Stack implements DataFlow.Something<Stack> {

private final int value;
private final String source;

private Stack(final int value) {
this(value, "");
}

private Stack(final BytecodeInstruction instruction) {
this(instruction.impact(), instruction.testCode());
}

private Stack(final int value, final String source) {
this.value = value;
this.source = source;
}

@Override
public int compareTo(final Stack o) {
return Integer.compare(this.value, o.value);
}

@Override
public Stack add(final Stack other) {
return new Stack(this.value + other.value, other.source);
}

@Override
public Stack initBlock() {
// return this.add(new Stack(1));
return new Stack(1);
}
}
}

0 comments on commit b35719e

Please sign in to comment.