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 }