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 jdk.incubator.code.Op; 25 import jdk.incubator.code.Quoted; 26 import jdk.incubator.code.TypeElement; 27 import jdk.incubator.code.Value; 28 import jdk.incubator.code.analysis.Inliner; 29 import jdk.incubator.code.dialect.java.JavaOp; 30 import jdk.incubator.code.dialect.java.JavaType; 31 import jdk.incubator.code.dialect.java.MethodRef; 32 import jdk.incubator.code.interpreter.Interpreter; 33 import org.junit.jupiter.api.Assertions; 34 import org.junit.jupiter.api.Test; 35 36 import java.lang.invoke.MethodHandles; 37 import java.util.stream.Stream; 38 39 import static jdk.incubator.code.dialect.core.CoreOp.*; 40 import static jdk.incubator.code.dialect.core.CoreType.functionType; 41 import static jdk.incubator.code.dialect.java.MethodRef.method; 42 43 /* 44 * @test 45 * @modules jdk.incubator.code 46 * @run junit TestLinqUsingQuoted 47 */ 48 49 public class TestLinqUsingQuoted { 50 51 // Query interfaces 52 53 public interface Queryable { 54 TypeElement elementType(); 55 56 // Queryable<T> -> Queryable<U> 57 FuncOp expression(); 58 59 QueryProvider provider(); 60 61 // T -> boolean 62 // Predicate<T> 63 default Queryable where(Quoted f) { 64 // @@@@ validate 65 ClosureOp c = (ClosureOp) f.op(); 66 return insertQuery(elementType(), "where", c); 67 } 68 69 // T -> R 70 // Function<T, R> 71 default Queryable select(Quoted f) { 72 // @@@@ validate 73 ClosureOp c = (ClosureOp) f.op(); 74 return insertQuery(c.invokableType().returnType(), "select", c); 75 } 76 77 private Queryable insertQuery(TypeElement et, String name, ClosureOp c) { 78 QueryProvider qp = provider(); 79 80 FuncOp currentQueryExpression = expression(); 81 FuncOp nextQueryExpression = currentQueryExpression.transform((block, op) -> { 82 if (op instanceof ReturnOp rop && rop.ancestorBody() == currentQueryExpression.body()) { 83 Value query = block.context().getValue(rop.returnValue()); 84 85 Op.Result quotedLambda = block.op(quoted(block.parentBody(), qblock -> c)); 86 87 MethodRef md = method(qp.queryableType(), name, 88 functionType(qp.queryableType(), QuotedOp.QUOTED_TYPE)); 89 Op.Result queryable = block.op(JavaOp.invoke(md, query, quotedLambda)); 90 91 block.op(return_(queryable)); 92 } else { 93 block.op(op); 94 } 95 return block; 96 }); 97 98 return qp.createQuery(et, nextQueryExpression); 99 } 100 101 // Iterate 102 // Queryable -> Stream 103 default QueryResult elements() { 104 TypeElement resultType = JavaType.parameterized(JavaType.type(Stream.class), (JavaType) elementType()); 105 return insertQueryResult("elements", resultType); 106 } 107 108 // Count 109 // Queryable -> Long 110 default QueryResult count() { 111 return insertQueryResult("count", JavaType.LONG); 112 } 113 114 private QueryResult insertQueryResult(String name, TypeElement resultType) { 115 QueryProvider qp = provider(); 116 117 // Copy function expression, replacing return type 118 FuncOp currentQueryExpression = expression(); 119 FuncOp nextQueryExpression = func("queryresult", 120 functionType(qp.queryResultType(), currentQueryExpression.invokableType().parameterTypes())) 121 .body(b -> Inliner.inline(b, currentQueryExpression, b.parameters(), (block, query) -> { 122 MethodRef md = method(qp.queryableType(), name, functionType(qp.queryResultType())); 123 Op.Result queryResult = block.op(JavaOp.invoke(md, query)); 124 125 block.op(return_(queryResult)); 126 })); 127 return qp.createQueryResult(resultType, nextQueryExpression); 128 } 129 } 130 131 public interface QueryResult { 132 TypeElement resultType(); 133 134 // Queryable -> QueryResult 135 FuncOp expression(); 136 137 Object execute(); 138 } 139 140 public interface QueryProvider { 141 TypeElement queryableType(); 142 143 TypeElement queryResultType(); 144 145 Queryable createQuery(TypeElement elementType, FuncOp expression); 146 147 QueryResult createQueryResult(TypeElement resultType, FuncOp expression); 148 149 Queryable newQuery(TypeElement elementType); 150 } 151 152 153 // Query implementation 154 155 public static final class TestQueryable implements Queryable { 156 final TypeElement elementType; 157 final TestQueryProvider provider; 158 final FuncOp expression; 159 160 TestQueryable(TypeElement elementType, TestQueryProvider provider) { 161 this.elementType = elementType; 162 this.provider = provider; 163 164 // Initial expression is an identity function 165 var funType = functionType(provider().queryableType(), provider().queryableType()); 166 this.expression = func("query", funType) 167 .body(b -> b.op(return_(b.parameters().get(0)))); 168 } 169 170 TestQueryable(TypeElement elementType, TestQueryProvider provider, FuncOp expression) { 171 this.elementType = elementType; 172 this.provider = provider; 173 this.expression = expression; 174 } 175 176 @Override 177 public TypeElement elementType() { 178 return elementType; 179 } 180 181 @Override 182 public FuncOp expression() { 183 return expression; 184 } 185 186 @Override 187 public QueryProvider provider() { 188 return provider; 189 } 190 } 191 192 public record TestQueryResult(TypeElement resultType, FuncOp expression) implements QueryResult { 193 @Override 194 public Object execute() { 195 // @@@ Compile/translate the expression and execute it 196 throw new UnsupportedOperationException(); 197 } 198 } 199 200 public static final class TestQueryProvider implements QueryProvider { 201 final TypeElement queryableType; 202 final TypeElement queryResultType; 203 204 TestQueryProvider() { 205 this.queryableType = JavaType.type(Queryable.class); 206 this.queryResultType = JavaType.type(QueryResult.class); 207 } 208 209 @Override 210 public TypeElement queryableType() { 211 return queryableType; 212 } 213 214 @Override 215 public TypeElement queryResultType() { 216 return queryResultType; 217 } 218 219 @Override 220 public TestQueryable createQuery(TypeElement elementType, FuncOp expression) { 221 return new TestQueryable(elementType, this, expression); 222 } 223 224 @Override 225 public QueryResult createQueryResult(TypeElement resultType, FuncOp expression) { 226 return new TestQueryResult(resultType, expression); 227 } 228 229 @Override 230 public Queryable newQuery(TypeElement elementType) { 231 return new TestQueryable(elementType, this); 232 } 233 } 234 235 236 static class Customer { 237 // 1st column 238 String contactName; 239 // 2nd column 240 String phone; 241 // 3rd column 242 String city; 243 } 244 245 @Test 246 public void testSimpleQuery() { 247 QueryProvider qp = new TestQueryProvider(); 248 249 QueryResult qr = qp.newQuery(JavaType.type(Customer.class)) 250 // c -> c.city.equals("London") 251 .where((Customer c) -> c.city.equals("London")) 252 // c -> c.contactName 253 .select((Customer c) -> c.contactName).elements(); 254 255 Op op1 = qr.expression(); 256 System.out.println(op1.toText()); 257 258 QueryResult qr2 = (QueryResult) Interpreter.invoke(MethodHandles.lookup(), 259 qr.expression(), qp.newQuery(JavaType.type(Customer.class))); 260 261 Op op = qr2.expression(); 262 System.out.println(op.toText()); 263 264 Assertions.assertEquals(qr2.expression().toText(), qr.expression().toText()); 265 } 266 }