1 /* 2 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import jdk.incubator.code.*; 25 import jdk.incubator.code.analysis.NormalizeBlocksTransformer; 26 import jdk.incubator.code.analysis.SSA; 27 import jdk.incubator.code.dialect.core.CoreOp; 28 import jdk.incubator.code.dialect.java.JavaOp; 29 import jdk.incubator.code.extern.OpParser; 30 import jdk.incubator.code.extern.OpWriter; 31 32 import java.io.StringWriter; 33 import java.lang.invoke.MethodHandles; 34 import java.lang.reflect.Member; 35 import java.lang.reflect.Method; 36 import java.util.HashSet; 37 import java.util.function.Predicate; 38 39 public class CodeReflectionTester { 40 41 public static void main(String[] args) throws ReflectiveOperationException { 42 if (args.length != 1) { 43 System.err.println("Usage: CodeReflectionTester <classname>"); 44 System.exit(1); 45 } 46 Class<?> clazz = Class.forName(args[0]); 47 48 Method lookupMethod = clazz.getMethod("lookup"); 49 MethodHandles.Lookup lookup = (MethodHandles.Lookup) lookupMethod.invoke(null); 50 51 Method opConstantsMethod = clazz.getMethod("opConstants"); 52 @SuppressWarnings("unchecked") 53 Predicate<Op> opConstants = (Predicate<Op>) opConstantsMethod.invoke(null); 54 55 for (Method m : clazz.getDeclaredMethods()) { 56 check(lookup, opConstants, m); 57 } 58 } 59 60 static void check(MethodHandles.Lookup l, Predicate<Op> opConstants, Method method) throws ReflectiveOperationException { 61 if (!method.isAnnotationPresent(CodeReflection.class)) { 62 return; 63 } 64 65 for (EvaluatedModel em : getModels(method)) { 66 CoreOp.FuncOp f = Op.ofMethod(method).orElseThrow(() -> 67 new AssertionError("No code model for reflective method")); 68 f = evaluate(l, opConstants, f, em.ssa()); 69 70 String actual = canonicalizeModel(method, f); 71 System.out.println(actual); 72 String expected = canonicalizeModel(method, em.value()); 73 if (!actual.equals(expected)) { 74 throw new AssertionError(String.format("Bad code model\nFound:\n%s\n\nExpected:\n%s", actual, expected)); 75 } 76 } 77 } 78 79 static EvaluatedModel[] getModels(Method method) { 80 EvaluatedModels ems = method.getAnnotation(EvaluatedModels.class); 81 if (ems != null) { 82 return ems.value(); 83 } 84 85 EvaluatedModel em = method.getAnnotation(EvaluatedModel.class); 86 if (em != null) { 87 return new EvaluatedModel[] { em }; 88 } 89 90 throw new AssertionError("No @EvaluatedModel annotation found on reflective method"); 91 } 92 93 static CoreOp.FuncOp evaluate(MethodHandles.Lookup l, Predicate<Op> opConstants, CoreOp.FuncOp f, boolean ssa) { 94 f = f.transform(OpTransformer.LOWERING_TRANSFORMER); 95 96 if (ssa) { 97 f = SSA.transform(f); 98 } 99 100 f = PartialEvaluator.evaluate(l, opConstants, new HashSet<>(), f); 101 102 return cleanUp(f); 103 } 104 105 static CoreOp.FuncOp cleanUp(CoreOp.FuncOp f) { 106 return removeUnusedOps(NormalizeBlocksTransformer.transform(f)); 107 } 108 109 static CoreOp.FuncOp removeUnusedOps(CoreOp.FuncOp f) { 110 Predicate<Op> unused = op -> (op instanceof Op.Pure || op instanceof CoreOp.VarOp) && 111 op.result().uses().isEmpty(); 112 while (f.elements().skip(1).anyMatch(ce -> ce instanceof Op op && unused.test(op))) { 113 f = f.transform((block, op) -> { 114 if (!unused.test(op)) { 115 block.op(op); 116 } 117 return block; 118 }); 119 } 120 return f; 121 } 122 123 // serializes dropping location information, parses, and then serializes, dropping location information 124 static String canonicalizeModel(Member m, Op o) { 125 return canonicalizeModel(m, serialize(o)); 126 } 127 128 // parses, and then serializes, dropping location information 129 static String canonicalizeModel(Member m, String d) { 130 Op o; 131 try { 132 o = OpParser.fromString(JavaOp.JAVA_DIALECT_FACTORY, d).get(0); 133 } catch (Exception e) { 134 throw new IllegalStateException(m.toString(), e); 135 } 136 return serialize(o); 137 } 138 139 // serializes, dropping location information 140 static String serialize(Op o) { 141 StringWriter w = new StringWriter(); 142 OpWriter.writeTo(w, o, OpWriter.LocationOption.DROP_LOCATION); 143 return w.toString(); 144 } 145 }