1 import org.testng.Assert;
 2 import org.testng.annotations.Test;
 3 
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.code.*;
 6 import java.lang.reflect.code.analysis.SSA;
 7 import java.lang.reflect.code.interpreter.Interpreter;
 8 import java.lang.runtime.CodeReflection;
 9 import java.util.Optional;
10 import java.util.function.Predicate;
11 import java.util.stream.Stream;
12 
13 import static java.lang.reflect.code.op.CoreOp.FuncOp;
14 import static java.lang.reflect.code.op.CoreOp.VarAccessOp.VarLoadOp;
15 import static java.lang.reflect.code.op.CoreOp.VarAccessOp.VarStoreOp;
16 import static java.lang.reflect.code.op.CoreOp.VarOp;
17 
18 /*
19  * @test
20  * @run testng TestRemoveFinalVars
21  */
22 
23 public class TestRemoveFinalVars {
24 
25     @CodeReflection
26     static boolean f() {
27         final int x = 8; // final var
28         int y = x + 2; // final var
29         int z = y + 3; // non final var
30         z++;
31         return x == 8 && y == 10 && z == 14;
32     }
33 
34     @Test
35     void test() {
36         FuncOp f = getFuncOp(this.getClass(),"f");
37         f.writeTo(System.out);
38         FuncOp lf = lower(f);
39         lf.writeTo(System.out);
40 
41         FuncOp f2 = f.transform(TestRemoveFinalVars::rmFinalVars);
42         f2.writeTo(System.out);
43         FuncOp lf2 = lower(f2);
44         lf2.writeTo(System.out);
45 
46         Assert.assertEquals(Interpreter.invoke(lf), Interpreter.invoke(lf2));
47 
48         SSA.transform(lower(f)).writeTo(System.out);
49     }
50 
51     static FuncOp lower(FuncOp funcOp) {
52         return funcOp.transform(OpTransformer.LOWERING_TRANSFORMER);
53     }
54 
55     static Block.Builder rmFinalVars(Block.Builder block, Op op) {
56         if (op instanceof VarOp varOp) {
57             // Is the variable stored to? If not we can remove it
58             // otherwise, it's not considered final and we copy it
59             if (isValueUsedWithOp(varOp.result(), o -> o instanceof VarStoreOp)) {
60                 block.op(varOp);
61             }
62         } else if (op instanceof VarLoadOp varLoadOp) {
63             // If the variable is not stored to
64             if (!isValueUsedWithOp(varLoadOp.varOp().result(), o -> o instanceof VarStoreOp)) {
65                 // Map result of load from variable to the value that initialized the variable
66                 // Subsequently encountered input operations using the result will be copied
67                 // to output operations using the mapped value
68                 CopyContext cc = block.context();
69                 cc.mapValue(varLoadOp.result(), cc.getValue(varLoadOp.varOp().operands().get(0)));
70             } else {
71                 block.op(varLoadOp);
72             }
73         } else {
74             block.op(op);
75         }
76         return block;
77     }
78 
79     private static boolean isValueUsedWithOp(Value value, Predicate<Op> opPredicate) {
80         for (Op.Result user : value.uses()) {
81             if (opPredicate.test(user.op())) {
82                 return true;
83             }
84         }
85         return false;
86     }
87 
88     static FuncOp getFuncOp(Class<?> c, String name) {
89         Optional<Method> om = Stream.of(c.getDeclaredMethods())
90                 .filter(m -> m.getName().equals(name))
91                 .findFirst();
92 
93         Method m = om.get();
94         return m.getCodeModel().get();
95     }
96 }