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 com.sun.tools.javac.api.JavacScope; 29 import com.sun.tools.javac.api.JavacTrees; 30 import com.sun.tools.javac.code.Symbol.ClassSymbol; 31 import com.sun.tools.javac.comp.Attr; 32 import com.sun.tools.javac.model.JavacElements; 33 import com.sun.tools.javac.processing.JavacProcessingEnvironment; 34 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 35 import com.sun.tools.javac.tree.TreeMaker; 36 import com.sun.tools.javac.util.Context; 37 import jdk.incubator.code.dialect.core.CoreType; 38 import jdk.incubator.code.internal.ReflectMethods; 39 import jdk.incubator.code.dialect.core.CoreOp.FuncOp; 40 import jdk.incubator.code.dialect.core.FunctionType; 41 import jdk.incubator.code.dialect.java.MethodRef; 42 import jdk.incubator.code.extern.OpWriter; 43 import jdk.internal.access.SharedSecrets; 44 45 import javax.annotation.processing.ProcessingEnvironment; 46 import javax.lang.model.element.ExecutableElement; 47 import javax.lang.model.element.Modifier; 48 import java.lang.reflect.InvocationTargetException; 49 import java.lang.reflect.Method; 50 import java.lang.reflect.Proxy; 51 import java.util.*; 52 import java.util.function.BiFunction; 53 54 /** 55 * An operation modelling a unit of functionality. 56 * <p> 57 * An operation might model the addition of two 32-bit integers, or a Java method call. 58 * Alternatively an operation may model something more complex like method bodies, lambda bodies, or 59 * try/catch/finally statements. In this case such an operation will contain one or more bodies modelling 60 * the nested structure. 61 */ 62 public non-sealed abstract class Op implements CodeElement<Op, Body> { 63 64 /** 65 * An operation characteristic indicating the operation is pure and has no side effects. 66 */ 67 public interface Pure { 68 } 69 70 /** 71 * An operation characteristic indicating the operation has one or more bodies. 72 */ 73 public interface Nested { 74 List<Body> bodies(); 75 } 76 77 /** 78 * An operation characteristic indicating the operation represents a loop 79 */ 80 public interface Loop extends Nested { 81 Body loopBody(); 82 } 83 84 /** 85 * An operation characteristic indicating the operation has one or more bodies, 86 * all of which are isolated. 87 */ 88 public interface Isolated extends Nested { 89 } 90 91 /** 92 * An operation characteristic indicating the operation is invokable, so the operation may be interpreted 93 * or compiled. 94 */ 95 public interface Invokable extends Nested { 96 /** 97 * {@return the body of the invokable operation.} 98 */ 99 Body body(); 100 101 /** 102 * {@return the function type describing the invokable operation's parameter types and return type.} 103 */ 104 FunctionType invokableType(); 105 106 /** 107 * {@return the entry block parameters of this operation's body} 108 */ 109 default List<Block.Parameter> parameters() { 110 return body().entryBlock().parameters(); 111 } 112 113 /** 114 * Computes values captured by this invokable operation's body. 115 * 116 * @return the captured values. 117 * @see Body#capturedValues() 118 */ 119 default List<Value> capturedValues() { 120 return List.of(); 121 } 122 } 123 124 /** 125 * An operation characteristic indicating the operation can replace itself with a lowered form. 126 */ 127 // @@@ Hide this abstraction within JavaOp? 128 public interface Lowerable { 129 130 /** 131 * Lowers this operation into the block builder, commonly replacing nested structure 132 * with interconnected basic blocks. The previous lowering code transformation 133 * is used to compose with a lowering transformation that is applied to bodies 134 * of this operation, ensuring lowering is applied consistently to nested content. 135 * 136 * @param b the block builder 137 * @param opT the previous lowering code transformation, may be {@code null} 138 * @return the block builder to use for further building 139 */ 140 Block.Builder lower(Block.Builder b, OpTransformer opT); 141 142 /** 143 * Returns a composed code transformer that composes with an operation transformer function adapted to lower 144 * operations. 145 * <p> 146 * This method behaves as if it returns the result of the following expression: 147 * {@snippet lang = java: 148 * OpTransformer.andThen(before, lowering(before, f)); 149 *} 150 * 151 * @param before the code transformer to apply before 152 * @param f the operation transformer function to apply after 153 * @return the composed code transformer 154 */ 155 static OpTransformer andThenLowering(OpTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) { 156 return OpTransformer.andThen(before, lowering(before, f)); 157 } 158 159 /** 160 * Returns an adapted operation transformer function that adapts an operation transformer function 161 * {@code f} to also transform lowerable operations. 162 * <p> 163 * The adapted operation transformer function first applies a block builder and operation 164 * to the operation transformer function {@code f}. 165 * If the result is not {@code null} then the result is returned. 166 * Otherwise, if the operation is a lowerable operation then the result of applying the 167 * block builder and code transformer {@code before} to {@link Lowerable#lower lower} 168 * of the lowerable operation is returned. 169 * Otherwise, the operation is copied by applying it to {@link Block.Builder#op op} of the block builder, 170 * and the block builder is returned. 171 * 172 * @param before the code transformer to apply for lowering 173 * @param f the operation transformer function to apply after 174 * @return the adapted operation transformer function 175 */ 176 static BiFunction<Block.Builder, Op, Block.Builder> lowering(OpTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) { 177 return (block, op) -> { 178 Block.Builder b = f.apply(block, op); 179 if (b == null) { 180 if (op instanceof Lowerable lop) { 181 block = lop.lower(block, before); 182 } else { 183 block.op(op); 184 } 185 } else { 186 block = b; 187 } 188 return block; 189 }; 190 } 191 } 192 193 /** 194 * An operation characteristic indicating the operation is a terminating operation 195 * occurring as the last operation in a block. 196 * <p> 197 * A terminating operation passes control to either another block within the same parent body 198 * or to that parent body. 199 */ 200 public interface Terminating { 201 } 202 203 /** 204 * An operation characteristic indicating the operation is a body terminating operation 205 * occurring as the last operation in a block. 206 * <p> 207 * A body terminating operation passes control back to its nearest ancestor body. 208 */ 209 public interface BodyTerminating extends Terminating { 210 } 211 212 /** 213 * An operation characteristic indicating the operation is a block terminating operation 214 * occurring as the last operation in a block. 215 * <p> 216 * The operation has one or more successors to other blocks within the same parent body, and passes 217 * control to one of those blocks. 218 */ 219 public interface BlockTerminating extends Terminating { 220 List<Block.Reference> successors(); 221 } 222 223 /** 224 * A value that is the result of an operation. 225 */ 226 public static final class Result extends Value { 227 228 /** 229 * If assigned to an operation result, it indicates the operation is sealed 230 */ 231 private static final Result SEALED_RESULT = new Result(); 232 233 final Op op; 234 235 private Result() { 236 super(null, null); 237 this.op = null; 238 } 239 240 Result(Block block, Op op) { 241 super(block, op.resultType()); 242 243 this.op = op; 244 } 245 246 @Override 247 public String toString() { 248 return "%result@" + Integer.toHexString(hashCode()); 249 } 250 251 @Override 252 public Set<Value> dependsOn() { 253 Set<Value> depends = new LinkedHashSet<>(op.operands()); 254 if (op instanceof Terminating) { 255 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add); 256 } 257 258 return Collections.unmodifiableSet(depends); 259 } 260 261 /** 262 * Returns the result's operation. 263 * 264 * @return the result's operation. 265 */ 266 public Op op() { 267 return op; 268 } 269 } 270 271 // Set when op is bound to block, otherwise null when unbound 272 // @@@ stable value? 273 Result result; 274 275 // null if not specified 276 // @@@ stable value? 277 Location location; 278 279 final List<Value> operands; 280 281 /** 282 * Constructs an operation from a given operation. 283 * <p> 284 * The constructor defers to the {@link Op#Op(List) operands} constructor passing a list of values computed, in 285 * order, by mapping the given operation's operands using the copy context. The constructor also assigns the new 286 * operation's location to the given operation's location, if any. 287 * 288 * @param that the given operation. 289 * @param cc the copy context. 290 */ 291 protected Op(Op that, CopyContext cc) { 292 this(cc.getValues(that.operands)); 293 this.location = that.location; 294 } 295 296 /** 297 * Copies this operation and transforms its bodies, if any. 298 * <p> 299 * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and 300 * operation transformer. 301 * @apiNote 302 * To copy an operation use the {@link OpTransformer#COPYING_TRANSFORMER copying transformer}. 303 * 304 * @param cc the copy context. 305 * @param ot the operation transformer. 306 * @return the transformed operation. 307 */ 308 public abstract Op transform(CopyContext cc, OpTransformer ot); 309 310 /** 311 * Constructs an operation with a list of operands. 312 * 313 * @param operands the list of operands, a copy of the list is performed if required. 314 */ 315 protected Op(List<? extends Value> operands) { 316 this.operands = List.copyOf(operands); 317 } 318 319 /** 320 * Sets the originating source location of this operation, if unbound. 321 * 322 * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified. 323 * @throws IllegalStateException if this operation is bound or sealed 324 */ 325 public final void setLocation(Location l) { 326 // @@@ Fail if location != null? 327 if (isSealed() || (result != null && result.block.isBound())) { 328 throw new IllegalStateException(); 329 } 330 331 location = l; 332 } 333 334 /** 335 * {@return the originating source location of this operation, otherwise {@code null} if not specified} 336 */ 337 public final Location location() { 338 return location; 339 } 340 341 /** 342 * Returns this operation's parent block, otherwise {@code null} if the operation is unbound or sealed. 343 * 344 * @return operation's parent block, or {@code null} if the operation is unbound or sealed. 345 */ 346 @Override 347 public final Block parent() { 348 if (isSealed() || result == null) { 349 return null; 350 } 351 352 if (!result.block.isBound()) { 353 throw new IllegalStateException("Parent block is partially constructed"); 354 } 355 356 return result.block; 357 } 358 359 @Override 360 public final List<Body> children() { 361 return bodies(); 362 } 363 364 /** 365 * {@return the operation's bodies, as an unmodifiable list} 366 * @implSpec this implementation returns an unmodifiable empty list. 367 */ 368 public List<Body> bodies() { 369 return List.of(); 370 } 371 372 /** 373 * {@return the operation's result type} 374 */ 375 public abstract TypeElement resultType(); 376 377 /** 378 * Returns the operation's result, otherwise {@code null} if the operation is unbound or sealed. 379 * 380 * @return the operation's result, or {@code null} if unbound or sealed. 381 */ 382 public final Result result() { 383 return result == Result.SEALED_RESULT ? null : result; 384 } 385 386 /** 387 * {@return the operation's operands, as an unmodifiable list} 388 */ 389 public final List<Value> operands() { 390 return operands; 391 } 392 393 /** 394 * {@return the operation's successors, as an unmodifiable list} 395 * @implSpec this implementation returns an unmodifiable empty list. 396 */ 397 public List<Block.Reference> successors() { 398 return List.of(); 399 } 400 401 /** 402 * Returns the operation's function type. 403 * <p> 404 * The function type's result type is the operation's result type and the function type's parameter types are the 405 * operation's operand types, in order. 406 * 407 * @return the function type 408 */ 409 public final FunctionType opType() { 410 List<TypeElement> operandTypes = operands.stream().map(Value::type).toList(); 411 return CoreType.functionType(resultType(), operandTypes); 412 } 413 414 /** 415 * Computes values captured by this operation. A captured value is a value that dominates 416 * this operation and is used by a descendant operation. 417 * <p> 418 * The order of the captured values is first use encountered in depth 419 * first search of this operation's descendant operations. 420 * 421 * @return the list of captured values, modifiable 422 * @see Body#capturedValues() 423 */ 424 public final List<Value> capturedValues() { 425 Set<Value> cvs = new LinkedHashSet<>(); 426 427 Deque<Body> bodyStack = new ArrayDeque<>(); 428 for (Body childBody : bodies()) { 429 Body.capturedValues(cvs, bodyStack, childBody); 430 } 431 return new ArrayList<>(cvs); 432 } 433 434 /** 435 * Seals this operation. After this operation is sealed its {@link #result result} and {@link #parent parent} are guaranteed to always be {@code null}. 436 * <p> 437 * If a sealed operation is {@link Block.Builder#op appended} to a {@link Block.Builder} then it is 438 * treated as if the operation is bound, and therefore the sealed operation will be transformed. 439 * <p> 440 * Sealing is idempotent if the operation is already sealed. 441 * 442 * @throws IllegalStateException if this operation is bound. 443 */ 444 public final void seal() { 445 if (result == Result.SEALED_RESULT) { 446 return; 447 } 448 if (result != null) { 449 throw new IllegalStateException("Operation cannot be sealed since it bound to a parent block"); 450 } 451 result = Result.SEALED_RESULT; 452 } 453 454 /** 455 * Returns {@code true} if this operation is sealed. 456 * @return {@code true} if this operation is sealed. 457 * @see #seal() 458 * */ 459 public final boolean isSealed() { 460 return result == Result.SEALED_RESULT; 461 } 462 463 /** 464 * Externalizes this operation's name as a string. 465 * @implSpec this implementation returns the result of the expression {@code this.getClass().getName()}. 466 * @return the operation name 467 */ 468 public String externalizeOpName() { 469 return this.getClass().getName(); 470 } 471 472 /** 473 * Externalizes this operation's specific state as a map of attributes. 474 * 475 * <p>A null attribute value is represented by the constant 476 * value {@link jdk.incubator.code.extern.ExternalizedOp#NULL_ATTRIBUTE_VALUE}. 477 * @implSpec this implementation returns an unmodifiable empty map. 478 * 479 * @return the operation's externalized state, as an unmodifiable map 480 */ 481 public Map<String, Object> externalize() { 482 return Map.of(); 483 } 484 485 /** 486 * Returns the textual form of this operation. 487 * 488 * @return the textual form of this operation. 489 */ 490 public final String toText() { 491 return OpWriter.toText(this); 492 } 493 494 495 /** 496 * Returns the quoted code model of the given quotable reference, if present. 497 * 498 * @param q the quotable reference. 499 * @return the quoted code model or an empty optional if the 500 * quoted code model is unavailable. 501 * @apiNote If the quotable reference is a proxy instance, then the 502 * quoted code model is unavailable and this method 503 * returns an empty optional. 504 * @since 99 505 */ 506 public static Optional<Quoted> ofQuotable(Quotable q) { 507 Object oq = q; 508 if (Proxy.isProxyClass(oq.getClass())) { 509 // @@@ The interpreter implements interpretation of 510 // lambdas using a proxy whose invocation handler 511 // supports the internal protocol to access the quoted instance 512 oq = Proxy.getInvocationHandler(oq); 513 } 514 515 Method method; 516 try { 517 method = oq.getClass().getDeclaredMethod("__internal_quoted"); 518 } catch (NoSuchMethodException e) { 519 return Optional.empty(); 520 } 521 method.setAccessible(true); 522 523 Quoted quoted; 524 try { 525 quoted = (Quoted) method.invoke(oq); 526 } catch (InvocationTargetException | IllegalAccessException e) { 527 throw new RuntimeException(e); 528 } 529 return Optional.of(quoted); 530 } 531 532 /** 533 * Returns the code model of the given method's body, if present. 534 * 535 * @param method the method. 536 * @return the code model of the method body. 537 * @since 99 538 */ 539 // @@@ Make caller sensitive with the same access control as invoke 540 // and throwing IllegalAccessException 541 // @CallerSensitive 542 @SuppressWarnings("unchecked") 543 public static Optional<FuncOp> ofMethod(Method method) { 544 return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess() 545 .setCodeModelIfNeeded(method, Op::createCodeModel); 546 } 547 548 private static Optional<FuncOp> createCodeModel(Method method) { 549 char[] sig = MethodRef.method(method).toString().toCharArray(); 550 for (int i = 0; i < sig.length; i++) { 551 switch (sig[i]) { 552 case '.', ';', '[', '/': sig[i] = '$'; 553 } 554 } 555 String opMethodName = new String(sig); 556 Method opMethod; 557 try { 558 // @@@ Use method handle with full power mode 559 opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName); 560 } catch (NoSuchMethodException e) { 561 return Optional.empty(); 562 } 563 opMethod.setAccessible(true); 564 try { 565 FuncOp funcOp = (FuncOp) opMethod.invoke(null); 566 return Optional.of(funcOp); 567 } catch (ReflectiveOperationException e) { 568 throw new RuntimeException(e); 569 } 570 } 571 572 /** 573 * Returns the code model of provided executable element (if any). 574 * <p> 575 * If the executable element has a code model then it will be an instance of 576 * {@code java.lang.reflect.code.op.CoreOps.FuncOp}. 577 * Note: due to circular dependencies we cannot refer to the type explicitly. 578 * 579 * @implSpec The default implementation unconditionally returns an empty optional. 580 * @param e the executable element. 581 * @return the code model of the provided executable element (if any). 582 * @since 99 583 */ 584 public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) { 585 if (e.getModifiers().contains(Modifier.ABSTRACT) || 586 e.getModifiers().contains(Modifier.NATIVE)) { 587 return Optional.empty(); 588 } 589 590 Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext(); 591 ReflectMethods reflectMethods = ReflectMethods.instance(context); 592 Attr attr = Attr.instance(context); 593 JavacElements elements = JavacElements.instance(context); 594 JavacTrees javacTrees = JavacTrees.instance(context); 595 TreeMaker make = TreeMaker.instance(context); 596 try { 597 JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e); 598 JavacScope scope = javacTrees.getScope(javacTrees.getPath(e)); 599 ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass(); 600 FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree, 601 attribBlock -> { 602 try { 603 return reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make); 604 } catch (Throwable ex) { 605 // this might happen if the source code contains errors 606 return null; 607 } 608 }); 609 return Optional.ofNullable(op); 610 } catch (RuntimeException ex) { // ReflectMethods.UnsupportedASTException 611 // some other error occurred when attempting to attribute the method 612 // @@@ better report of error 613 ex.printStackTrace(); 614 return Optional.empty(); 615 } 616 } 617 }