1 /* 2 * Copyright (c) 2024, 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 java.io.StringWriter; 25 import java.lang.reflect.Field; 26 import java.lang.reflect.Member; 27 import java.lang.reflect.Method; 28 import jdk.incubator.code.*; 29 import jdk.incubator.code.op.ExtendedOp; 30 import jdk.incubator.code.parser.OpParser; 31 import jdk.incubator.code.writer.OpWriter; 32 import jdk.incubator.code.CodeReflection; 33 34 import static jdk.incubator.code.op.CoreOp._return; 35 import static jdk.incubator.code.op.CoreOp.func; 36 import static jdk.incubator.code.type.FunctionType.VOID; 37 38 public class CodeReflectionTester { 39 40 static int nErrors = 0; 41 42 public static void main(String[] args) throws ReflectiveOperationException { 43 if (args.length != 1) { 44 System.err.println("Usage: CodeReflectionTester <classname>"); 45 System.exit(1); 46 } 47 Class<?> clazz = Class.forName(args[0]); 48 for (Method m : clazz.getDeclaredMethods()) { 49 check(m); 50 } 51 for (Field f : clazz.getDeclaredFields()) { 52 check(f); 53 } 54 if (nErrors > 0) { 55 throw new AssertionError("Test failed with " + nErrors + " errors"); 56 } 57 } 58 59 static void error(String msg, Object... args) { 60 nErrors++; 61 System.err.println("error: " + String.format(msg, args)); 62 } 63 64 static void check(Method method) throws ReflectiveOperationException { 65 if (!method.isAnnotationPresent(CodeReflection.class)) return; 66 String found = canonicalizeModel(method, Op.ofMethod(method).orElseThrow()); 67 IR ir = method.getAnnotation(IR.class); 68 if (ir == null) { 69 error("No @IR annotation found on reflective method"); 70 return; 71 } 72 String expected = canonicalizeModel(method, ir.value()); 73 if (!found.equals(expected)) { 74 error("Bad IR\nFound:\n%s\n\nExpected:\n%s", found, expected); 75 return; 76 } 77 } 78 79 static void check(Field field) throws ReflectiveOperationException { 80 IR ir = field.getAnnotation(IR.class); 81 if (ir == null) return; 82 if (field.getType().equals(Quoted.class)) { 83 // transitional 84 Quoted quoted = (Quoted) field.get(null); 85 String found = canonicalizeModel(field, getModelOfQuotedOp(quoted)); 86 String expected = canonicalizeModel(field, ir.value()); 87 if (!found.equals(expected)) { 88 error("Bad IR\nFound:\n%s\n\nExpected:\n%s", found, expected); 89 return; 90 } 91 } else if (Quotable.class.isAssignableFrom(field.getType())) { 92 Quotable quotable = (Quotable) field.get(null); 93 String found = canonicalizeModel(field, getModelOfQuotedOp(quotable.quoted())); 94 String expected = canonicalizeModel(field, ir.value()); 95 if (!found.equals(expected)) { 96 error("Bad IR\nFound:\n%s\n\nExpected:\n%s", found, expected); 97 return; 98 } 99 } else { 100 error("Field annotated with @IR should be of a quotable type (Quoted/Quotable)"); 101 return; 102 } 103 } 104 105 // serializes dropping location information, parses, and then serializes, dropping location information 106 static String canonicalizeModel(Member m, Op o) { 107 return canonicalizeModel(m, serialize(o)); 108 } 109 110 // parses, and then serializes, dropping location information 111 static String canonicalizeModel(Member m, String d) { 112 Op o; 113 try { 114 o = OpParser.fromString(ExtendedOp.FACTORY, d).get(0); 115 } catch (Exception e) { 116 throw new IllegalStateException(m.toString(), e); 117 } 118 return serialize(o); 119 } 120 121 // serializes, dropping location information 122 static String serialize(Op o) { 123 StringWriter w = new StringWriter(); 124 OpWriter.writeTo(w, o, OpWriter.LocationOption.DROP_LOCATION); 125 return w.toString(); 126 } 127 128 static Op getModelOfQuotedOp(Quoted quoted) { 129 return func("f", VOID).body(fblock -> { 130 CopyContext cc = fblock.context(); 131 for (Value cv : quoted.capturedValues().keySet()) { 132 Block.Parameter p = fblock.parameter(cv.type()); 133 cc.mapValue(cv, p); 134 } 135 136 Op qOp = quoted.op(); 137 // Associate the quoted ops ancestor body's entry block 138 // with the function's entry block, thereby ensuring that 139 // captured values mapped to the function's parameters 140 // are reachable 141 cc.mapBlock(qOp.ancestorBody().entryBlock(), fblock); 142 fblock.op(qOp); 143 144 fblock.op(_return()); 145 }); 146 } 147 }