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