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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.incubator.code; 27 28 import java.io.OutputStream; 29 import java.io.OutputStreamWriter; 30 import java.io.Writer; 31 32 import com.sun.tools.javac.api.JavacScope; 33 import com.sun.tools.javac.api.JavacTrees; 34 import com.sun.tools.javac.code.Symbol.ClassSymbol; 35 import com.sun.tools.javac.comp.Attr; 36 import com.sun.tools.javac.model.JavacElements; 37 import com.sun.tools.javac.processing.JavacProcessingEnvironment; 38 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 39 import com.sun.tools.javac.tree.TreeMaker; 40 import com.sun.tools.javac.util.Context; 41 import jdk.incubator.code.internal.ReflectMethods; 42 import jdk.incubator.code.op.CoreOp.FuncOp; 43 import jdk.incubator.code.type.FunctionType; 44 import jdk.incubator.code.type.MethodRef; 45 import jdk.incubator.code.writer.OpWriter; 46 import jdk.internal.access.SharedSecrets; 47 48 import javax.annotation.processing.ProcessingEnvironment; 49 import javax.lang.model.element.ExecutableElement; 50 import javax.lang.model.element.Modifier; 51 import java.lang.reflect.InvocationTargetException; 52 import java.lang.reflect.Method; 53 import java.lang.reflect.Proxy; 54 import java.nio.charset.StandardCharsets; 55 import java.util.*; 56 import java.util.function.BiFunction; 57 58 /** 59 * An operation modelling a unit of functionality. 60 * <p> 61 * An operation might model the addition of two 32-bit integers, or a Java method call. 62 * Alternatively an operation may model something more complex like method bodies, lambda bodies, or 63 * try/catch/finally statements. In this case such an operation will contain one or more bodies modelling 64 * the nested structure. 65 */ 66 public non-sealed abstract class Op implements CodeElement<Op, Body> { 67 68 /** 69 * An operation characteristic indicating the operation is pure and has no side effects. 70 */ 71 public interface Pure { 72 } 73 74 /** 75 * An operation characteristic indicating the operation has one or more bodies. 76 */ 77 public interface Nested { 78 List<Body> bodies(); 79 } 80 81 /** 82 * An operation characteristic indicating the operation represents a loop 83 */ 84 public interface Loop extends Nested { 85 Body loopBody(); 86 } 87 88 /** 89 * An operation characteristic indicating the operation has one or more bodies, 90 * all of which are isolated. 91 */ 92 public interface Isolated extends Nested { 93 } 94 95 /** 96 * An operation characteristic indicating the operation is invokable, so the operation may be interpreted 97 * or compiled. 98 */ 99 public interface Invokable extends Nested { 100 /** 101 * {@return the body of the invokable operation.} 102 */ 103 Body body(); 104 105 /** 106 * {@return the function type describing the invokable operation's parameter types and return type.} 107 */ 108 FunctionType invokableType(); 109 110 /** 111 * {@return the entry block parameters of this operation's body} 112 */ 113 default List<Block.Parameter> parameters() { 114 return body().entryBlock().parameters(); 115 } 116 117 /** 118 * Computes values captured by this invokable operation's body. 119 * 120 * @return the captured values. 121 * @see Body#capturedValues() 122 */ 123 default List<Value> capturedValues() { 124 return List.of(); 125 } 126 } 127 128 /** 129 * An operation characteristic indicating the operation can replace itself with a lowered form, 130 * consisting only of operations in the core dialect. 131 */ 132 public interface Lowerable { 133 default Block.Builder lower(Block.Builder b) { 134 return lower(b, OpTransformer.NOOP_TRANSFORMER); 135 } 136 137 Block.Builder lower(Block.Builder b, OpTransformer opT); 138 } 139 140 /** 141 * An operation characteristic indicating the operation is a terminating operation 142 * occurring as the last operation in a block. 143 * <p> 144 * A terminating operation passes control to either another block within the same parent body 145 * or to that parent body. 146 */ 147 public interface Terminating { 148 } 149 150 /** 151 * An operation characteristic indicating the operation is a body terminating operation 152 * occurring as the last operation in a block. 153 * <p> 154 * A body terminating operation passes control back to its nearest ancestor body. 155 */ 156 public interface BodyTerminating extends Terminating { 157 } 158 159 /** 160 * An operation characteristic indicating the operation is a block terminating operation 161 * occurring as the last operation in a block. 162 * <p> 163 * The operation has one or more successors to other blocks within the same parent body, and passes 164 * control to one of those blocks. 165 */ 166 public interface BlockTerminating extends Terminating { 167 List<Block.Reference> successors(); 168 } 169 170 /** 171 * A value that is the result of an operation. 172 */ 173 public static final class Result extends Value { 174 final Op op; 175 176 Result(Block block, Op op) { 177 super(block, op.resultType()); 178 179 this.op = op; 180 } 181 182 @Override 183 public Set<Value> dependsOn() { 184 Set<Value> depends = new LinkedHashSet<>(op.operands()); 185 if (op instanceof Terminating) { 186 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add); 187 } 188 189 return Collections.unmodifiableSet(depends); 190 } 191 192 /** 193 * Returns the result's operation. 194 * 195 * @return the result's operation. 196 */ 197 public Op op() { 198 return op; 199 } 200 } 201 202 // Set when op is bound to block, otherwise null when unbound 203 Result result; 204 205 // null if not specified 206 Location location; 207 208 final String name; 209 210 final List<Value> operands; 211 212 /** 213 * Constructs an operation by copying given operation. 214 * 215 * @param that the operation to copy. 216 * @param cc the copy context. 217 * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list 218 * values computed, in order, by mapping the operation's operands using the copy context. 219 */ 220 protected Op(Op that, CopyContext cc) { 221 this(that.name, cc.getValues(that.operands)); 222 this.location = that.location; 223 } 224 225 /** 226 * Copies this operation and its bodies, if any. 227 * <p> 228 * The returned operation is structurally identical to this operation and is otherwise independent 229 * of the values declared and used. 230 * 231 * @return the copied operation. 232 */ 233 public Op copy() { 234 return transform(CopyContext.create(), OpTransformer.COPYING_TRANSFORMER); 235 } 236 237 /** 238 * Copies this operation and its bodies, if any. 239 * <p> 240 * The returned operation is structurally identical to this operation and is otherwise independent 241 * of the values declared and used. 242 * 243 * @param cc the copy context. 244 * @return the copied operation. 245 */ 246 public Op copy(CopyContext cc) { 247 return transform(cc, OpTransformer.COPYING_TRANSFORMER); 248 } 249 250 /** 251 * Copies this operation and transforms its bodies, if any. 252 * <p> 253 * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and 254 * operation transformer. 255 * 256 * @param cc the copy context. 257 * @param ot the operation transformer. 258 * @return the transformed operation. 259 */ 260 public abstract Op transform(CopyContext cc, OpTransformer ot); 261 262 /** 263 * Constructs an operation with a name and list of operands. 264 * 265 * @param name the operation name. 266 * @param operands the list of operands, a copy of the list is performed if required. 267 */ 268 protected Op(String name, List<? extends Value> operands) { 269 this.name = name; 270 this.operands = List.copyOf(operands); 271 } 272 273 /** 274 * Sets the originating source location of this operation, if unbound. 275 * 276 * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified. 277 * @throws IllegalStateException if this operation is bound 278 */ 279 public final void setLocation(Location l) { 280 // @@@ Fail if location != null? 281 if (result != null && result.block.isBound()) { 282 throw new IllegalStateException(); 283 } 284 285 location = l; 286 } 287 288 /** 289 * {@return the originating source location of this operation, otherwise {@code null} if not specified} 290 */ 291 public final Location location() { 292 return location; 293 } 294 295 /** 296 * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block. 297 * 298 * @return operation's parent block, or {@code null} if the operation is not assigned to a block. 299 */ 300 @Override 301 public final Block parent() { 302 return parentBlock(); 303 } 304 305 /** 306 * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block. 307 * 308 * @return operation's parent block, or {@code null} if the operation is not assigned to a block. 309 */ 310 public final Block parentBlock() { 311 if (result == null) { 312 return null; 313 } 314 315 if (!result.block.isBound()) { 316 throw new IllegalStateException("Parent block is partially constructed"); 317 } 318 319 return result.block; 320 } 321 322 @Override 323 public final List<Body> children() { 324 return bodies(); 325 } 326 327 /** 328 * {@return the operation's bodies, as an unmodifiable list} 329 * @implSpec this implementation returns an unmodifiable empty list. 330 */ 331 public List<Body> bodies() { 332 return List.of(); 333 } 334 335 /** 336 * Returns the operation's result, otherwise {@code null} if the operation is not assigned to a block. 337 * 338 * @return the operation's result, or {@code null} if not assigned to a block. 339 */ 340 public final Result result() { 341 return result; 342 } 343 344 345 /** 346 * Returns this operation's nearest ancestor body (the parent body of this operation's parent block), 347 * otherwise {@code null} if the operation is not assigned to a block. 348 * 349 * @return operation's nearest ancestor body, or {@code null} if the operation is not assigned to a block. 350 */ 351 public final Body ancestorBody() { 352 if (result == null) { 353 return null; 354 } 355 356 if (!result.block.isBound()) { 357 throw new IllegalStateException("Parent body is partially constructed"); 358 } 359 360 return result.block.parentBody; 361 } 362 363 /** 364 * {@return the operation name} 365 */ 366 public String opName() { 367 return name; 368 } 369 370 /** 371 * {@return the operation's operands, as an unmodifiable list} 372 */ 373 public List<Value> operands() { 374 return operands; 375 } 376 377 /** 378 * {@return the operation's successors, as an unmodifiable list} 379 */ 380 public List<Block.Reference> successors() { 381 return List.of(); 382 } 383 384 /** 385 * {@return the operation's result type} 386 */ 387 public abstract TypeElement resultType(); 388 389 /** 390 * Returns the operation's function type. 391 * <p> 392 * The function type's result type is the operation's result type and the function type's parameter types are the 393 * operation's operand types, in order. 394 * 395 * @return the function type 396 */ 397 public FunctionType opType() { 398 List<TypeElement> operandTypes = operands.stream().map(Value::type).toList(); 399 return FunctionType.functionType(resultType(), operandTypes); 400 } 401 402 /** 403 * Traverse the operands of this operation that are the results of prior operations, recursively. 404 * <p> 405 * Traversal is performed in pre-order, reporting the operation of each operand to the visitor. 406 * 407 * @param t the traversing accumulator 408 * @param v the visitor 409 * @param <T> accumulator type 410 * @return the traversing accumulator 411 * @apiNote A visitor that implements the abstract method of {@code OpVisitor} and does not override any 412 * other default method will only visit operations. As such a lambda expression or method reference 413 * may be used to visit operations. 414 */ 415 public final <T> T traverseOperands(T t, BiFunction<T, Op, T> v) { 416 for (Value arg : operands()) { 417 if (arg instanceof Result or) { 418 t = v.apply(t, or.op); 419 t = or.op.traverseOperands(t, v); 420 } 421 } 422 423 return t; 424 } 425 426 /** 427 * Computes values captured by this operation. A captured value is a value that dominates 428 * this operation and is used by a descendant operation. 429 * <p> 430 * The order of the captured values is first use encountered in depth 431 * first search of this operation's descendant operations. 432 * 433 * @return the list of captured values, modifiable 434 * @see Body#capturedValues() 435 */ 436 public List<Value> capturedValues() { 437 Set<Value> cvs = new LinkedHashSet<>(); 438 439 capturedValues(cvs, new ArrayDeque<>(), this); 440 return new ArrayList<>(cvs); 441 } 442 443 static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Op op) { 444 for (Body childBody : op.bodies()) { 445 Body.capturedValues(capturedValues, bodyStack, childBody); 446 } 447 } 448 449 /** 450 * Writes the textual form of this operation to the given output stream, using the UTF-8 character set. 451 * 452 * @param out the stream to write to. 453 */ 454 public void writeTo(OutputStream out) { 455 writeTo(new OutputStreamWriter(out, StandardCharsets.UTF_8)); 456 } 457 458 /** 459 * Writes the textual form of this operation to the given writer. 460 * 461 * @param w the writer to write to. 462 */ 463 public void writeTo(Writer w) { 464 OpWriter.writeTo(w, this); 465 } 466 467 /** 468 * Returns the textual form of this operation. 469 * 470 * @return the textual form of this operation. 471 */ 472 public String toText() { 473 return OpWriter.toText(this); 474 } 475 476 477 /** 478 * Returns the code model of the Quotable passed in. 479 * @param q the Quotable we want to get its code model. 480 * @return the code model of the Quotable passed in. 481 * @apiNote If the Quotable instance is a proxy instance, then the quoted code model is inaccessible and this method 482 * returns an empty optional. 483 * @since 99 484 */ 485 public static Optional<Quoted> ofQuotable(Quotable q) { 486 Object oq = q; 487 if (Proxy.isProxyClass(oq.getClass())) { 488 oq = Proxy.getInvocationHandler(oq); 489 } 490 491 Method method; 492 try { 493 method = oq.getClass().getMethod("__internal_quoted"); 494 } catch (NoSuchMethodException e) { 495 return Optional.empty(); 496 } 497 method.setAccessible(true); 498 499 Quoted quoted; 500 try { 501 quoted = (Quoted) method.invoke(oq); 502 } catch (InvocationTargetException | IllegalAccessException e) { 503 throw new RuntimeException(e); 504 } 505 return Optional.of(quoted); 506 } 507 508 /** 509 * Returns the code model of the method body, if present. 510 * @return the code model of the method body. 511 * @since 99 512 */ 513 // @@@ Make caller sensitive with the same access control as invoke 514 // and throwing IllegalAccessException 515 // @CallerSensitive 516 @SuppressWarnings("unchecked") 517 public static Optional<FuncOp> ofMethod(Method method) { 518 return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess() 519 .setCodeModelIfNeeded(method, Op::createCodeModel); 520 } 521 522 private static Optional<FuncOp> createCodeModel(Method method) { 523 char[] sig = MethodRef.method(method).toString().toCharArray(); 524 for (int i = 0; i < sig.length; i++) { 525 switch (sig[i]) { 526 case '.', ';', '[', '/': sig[i] = '$'; 527 } 528 } 529 String opMethodName = "op$" + new String(sig); 530 Method opMethod; 531 try { 532 // @@@ Use method handle with full power mode 533 opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName); 534 } catch (NoSuchMethodException e) { 535 return Optional.empty(); 536 } 537 opMethod.setAccessible(true); 538 try { 539 FuncOp funcOp = (FuncOp) opMethod.invoke(null); 540 return Optional.of(funcOp); 541 } catch (ReflectiveOperationException e) { 542 throw new RuntimeException(e); 543 } 544 } 545 546 /** 547 * Returns the code model of provided executable element (if any). 548 * <p> 549 * If the executable element has a code model then it will be an instance of 550 * {@code java.lang.reflect.code.op.CoreOps.FuncOp}. 551 * Note: due to circular dependencies we cannot refer to the type explicitly. 552 * 553 * @implSpec The default implementation unconditionally returns an empty optional. 554 * @param e the executable element. 555 * @return the code model of the provided executable element (if any). 556 * @since 99 557 */ 558 public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) { 559 if (e.getModifiers().contains(Modifier.ABSTRACT) || 560 e.getModifiers().contains(Modifier.NATIVE)) { 561 return Optional.empty(); 562 } 563 564 Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext(); 565 ReflectMethods reflectMethods = ReflectMethods.instance(context); 566 Attr attr = Attr.instance(context); 567 JavacElements elements = JavacElements.instance(context); 568 JavacTrees javacTrees = JavacTrees.instance(context); 569 TreeMaker make = TreeMaker.instance(context); 570 try { 571 JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e); 572 JavacScope scope = javacTrees.getScope(javacTrees.getPath(e)); 573 ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass(); 574 FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree, 575 attribBlock -> reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make)); 576 return Optional.of(op); 577 } catch (RuntimeException ex) { // ReflectMethods.UnsupportedASTException 578 // some other error occurred when attempting to attribute the method 579 // @@@ better report of error 580 ex.printStackTrace(); 581 return Optional.empty(); 582 } 583 } 584 }