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 /*
 25  * @test
 26  * @modules jdk.incubator.code
 27  * @run junit TestAncestors
 28  */
 29 
 30 import jdk.incubator.code.*;
 31 import jdk.incubator.code.Reflect;
 32 import jdk.incubator.code.dialect.core.CoreOp;
 33 import org.junit.jupiter.api.Assertions;
 34 import org.junit.jupiter.api.Test;
 35 
 36 import java.lang.reflect.Method;
 37 import java.util.ArrayList;
 38 import java.util.List;
 39 import java.util.Optional;
 40 import java.util.stream.Stream;
 41 
 42 import static java.lang.System.out;
 43 
 44 public class TestAncestors {
 45 
 46     // Model with sufficient nested structure
 47     @Reflect
 48     static void f() {
 49         out.println("X");
 50         {
 51             out.println("X");
 52             {
 53                 out.println("X");
 54             }
 55             out.println("X");
 56             {
 57                 out.println("X");
 58             }
 59             out.println("X");
 60         }
 61         out.println("X");
 62         {
 63             out.println("X");
 64             {
 65                 out.println("X");
 66             }
 67             out.println("X");
 68             {
 69                 out.println("X");
 70             }
 71             out.println("X");
 72         }
 73         out.println("X");
 74     }
 75 
 76     @Test
 77     public void test() {
 78         CoreOp.FuncOp f = getFuncOp("f");
 79         out.println(f.toText());
 80 
 81         List<List<CodeElement<?, ?>>> paths = new ArrayList<>();
 82         // path has pattern of [op, body, block, ... ,body, block, op]
 83         computedPaths(paths,List.of(), f);
 84 
 85         testPathPrefix(paths.getFirst());
 86         paths.forEach(TestAncestors::testPathAncestors);
 87     }
 88 
 89     static void testPathPrefix(List<CodeElement<?, ?>> path) {
 90         for (int i = 0; i < 3; i++) {
 91             CodeElement<?, ?> a = path.get(i);
 92             testTopElements(a);
 93         }
 94     }
 95 
 96     static void testTopElements(CodeElement<?, ?> a) {
 97         switch (a) {
 98             case Op op -> {
 99                 Assertions.assertNull(op.ancestorOp());
100                 Assertions.assertNull(op.ancestorBody());
101                 Assertions.assertNull(op.ancestorBlock());
102             }
103             case Body body -> {
104                 Assertions.assertNotNull(body.ancestorOp());
105                 Assertions.assertNull(body.ancestorBody());
106                 Assertions.assertNull(body.ancestorBlock());
107             }
108             case Block block -> {
109                 Assertions.assertNotNull(block.ancestorOp());
110                 Assertions.assertNotNull(block.ancestorBody());
111                 Assertions.assertNull(block.ancestorBlock());
112             }
113         }
114     }
115 
116     static void testPathAncestors(List<CodeElement<?, ?>> path) {
117         Assertions.assertTrue(path.size() > 3);
118         for (int i = 0; i < 3; i++) {
119             CodeElement<?, ?> a = path.get(i);
120             int size = path.size() - 1;
121             for (int j = size; j > size - 3; j--) {
122                 if (j < i) {
123                     continue;
124                 }
125 
126                 CodeElement<?, ?> e = path.get(j);
127                 testAncestors(a, e);
128             }
129         }
130     }
131 
132     static void testAncestors(CodeElement<?, ?> a, CodeElement<?, ?> e) {
133         Assertions.assertTrue(isSameOrAncestorUsingParent(e, a));
134         if (a != e) {
135             Assertions.assertTrue(a.isAncestorOf(e));
136         }
137 
138         switch (a) {
139             case Op op -> {
140                 Assertions.assertTrue(isSameOrAncestorOfOp(op, e));
141             }
142             case Body body -> {
143                 Assertions.assertTrue(isSameOrAncestorOfBody(body, e));
144             }
145             case Block block -> {
146                 Assertions.assertTrue(isSameOrAncestorOfBlock(block, e));
147             }
148         }
149     }
150 
151     static boolean isSameOrAncestorUsingParent(CodeElement<?, ?> e, CodeElement<?, ?> a) {
152         while (e != null && e != a) {
153             e = e.parent();
154         }
155         return e != null;
156     }
157 
158     static boolean isSameOrAncestorOfOp(Op a, CodeElement<?, ?> e) {
159         while (e != null && e != a) {
160             e = e.ancestorOp();
161         }
162         return e != null;
163     }
164 
165     static boolean isSameOrAncestorOfBody(Body a, CodeElement<?, ?> e) {
166         while (e != null && e != a) {
167             e = e.ancestorBody();
168         }
169         return e != null;
170     }
171 
172     static boolean isSameOrAncestorOfBlock(Block a, CodeElement<?, ?> e) {
173         while (e != null && e != a) {
174             e = e.ancestorBlock();
175         }
176         return e != null;
177     }
178 
179     static void computedPaths(List<List<CodeElement<?, ?>>> paths, List<CodeElement<?, ?>> path, CodeElement<?, ?> e) {
180         ArrayList<CodeElement<?, ?>> p = new ArrayList<>(path);
181         p.add(e);
182 
183         if (e.children().isEmpty()) {
184             paths.add(p);
185             return;
186         }
187 
188         for (CodeElement<?, ?> child : e.children()) {
189             computedPaths(paths, p, child);
190         }
191     }
192 
193     static CoreOp.FuncOp getFuncOp(String name) {
194         Optional<Method> om = Stream.of(TestAncestors.class.getDeclaredMethods())
195                 .filter(m -> m.getName().equals(name))
196                 .findFirst();
197 
198         Method m = om.get();
199         return Op.ofMethod(m).get();
200     }
201 }