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 final Op op; 228 229 Result(Block block, Op op) { 230 super(block, op.resultType()); 231 232 this.op = op; 233 } 234 235 @Override 236 public String toString() { 237 return "%result@" + Integer.toHexString(hashCode()); 238 } 239 240 @Override 241 public Set<Value> dependsOn() { 242 Set<Value> depends = new LinkedHashSet<>(op.operands()); 243 if (op instanceof Terminating) { 244 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add); 245 } 246 247 return Collections.unmodifiableSet(depends); 248 } 249 250 /** 251 * Returns the result's operation. 252 * 253 * @return the result's operation. 254 */ 255 public Op op() { 256 return op; 257 } 258 } 259 260 // Set when op is bound to block, otherwise null when unbound 261 Result result; 262 263 // null if not specified 264 Location location; 265 266 final String name; 267 268 final List<Value> operands; 269 270 /** 271 * Constructs an operation by copying given operation. 272 * 273 * @param that the operation to copy. 274 * @param cc the copy context. 275 * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list 276 * values computed, in order, by mapping the operation's operands using the copy context. 277 */ 278 protected Op(Op that, CopyContext cc) { 279 this(that.name, cc.getValues(that.operands)); 280 this.location = that.location; 281 } 282 283 /** 284 * Copies this operation and its bodies, if any. 285 * <p> 286 * The returned operation is structurally identical to this operation and is otherwise independent 287 * of the values declared and used. 288 * 289 * @return the copied operation. 290 */ 291 public Op copy() { 292 return transform(CopyContext.create(), OpTransformer.COPYING_TRANSFORMER); 293 } 294 295 /** 296 * Copies this operation and its bodies, if any. 297 * <p> 298 * The returned operation is structurally identical to this operation and is otherwise independent 299 * of the values declared and used. 300 * 301 * @param cc the copy context. 302 * @return the copied operation. 303 */ 304 public Op copy(CopyContext cc) { 305 return transform(cc, OpTransformer.COPYING_TRANSFORMER); 306 } 307 308 /** 309 * Copies this operation and transforms its bodies, if any. 310 * <p> 311 * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and 312 * operation transformer. 313 * 314 * @param cc the copy context. 315 * @param ot the operation transformer. 316 * @return the transformed operation. 317 */ 318 public abstract Op transform(CopyContext cc, OpTransformer ot); 319 320 /** 321 * Constructs an operation with a name and list of operands. 322 * 323 * @param name the operation name. 324 * @param operands the list of operands, a copy of the list is performed if required. 325 */ 326 protected Op(String name, List<? extends Value> operands) { 327 this.name = name; 328 this.operands = List.copyOf(operands); 329 } 330 331 /** 332 * Sets the originating source location of this operation, if unbound. 333 * 334 * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified. 335 * @throws IllegalStateException if this operation is bound 336 */ 337 public final void setLocation(Location l) { 338 // @@@ Fail if location != null? 339 if (result != null && result.block.isBound()) { 340 throw new IllegalStateException(); 341 } 342 343 location = l; 344 } 345 346 /** 347 * {@return the originating source location of this operation, otherwise {@code null} if not specified} 348 */ 349 public final Location location() { 350 return location; 351 } 352 353 /** 354 * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block. 355 * 356 * @return operation's parent block, or {@code null} if the operation is not assigned to a block. 357 */ 358 @Override 359 public final Block parent() { 360 if (result == null) { 361 return null; 362 } 363 364 if (!result.block.isBound()) { 365 throw new IllegalStateException("Parent block is partially constructed"); 366 } 367 368 return result.block; 369 } 370 371 @Override 372 public final List<Body> children() { 373 return bodies(); 374 } 375 376 /** 377 * {@return the operation's bodies, as an unmodifiable list} 378 * @implSpec this implementation returns an unmodifiable empty list. 379 */ 380 public List<Body> bodies() { 381 return List.of(); 382 } 383 384 /** 385 * Returns the operation's result, otherwise {@code null} if the operation is not assigned to a block. 386 * 387 * @return the operation's result, or {@code null} if not assigned to a block. 388 */ 389 public final Result result() { 390 return result; 391 } 392 393 /** 394 * {@return the operation name} 395 */ 396 public String opName() { 397 return name; 398 } 399 400 /** 401 * {@return the operation's operands, as an unmodifiable list} 402 */ 403 public List<Value> operands() { 404 return operands; 405 } 406 407 /** 408 * {@return the operation's successors, as an unmodifiable list} 409 */ 410 public List<Block.Reference> successors() { 411 return List.of(); 412 } 413 414 /** 415 * {@return the operation's result type} 416 */ 417 public abstract TypeElement resultType(); 418 419 /** 420 * Returns the operation's function type. 421 * <p> 422 * The function type's result type is the operation's result type and the function type's parameter types are the 423 * operation's operand types, in order. 424 * 425 * @return the function type 426 */ 427 public FunctionType opType() { 428 List<TypeElement> operandTypes = operands.stream().map(Value::type).toList(); 429 return CoreType.functionType(resultType(), operandTypes); 430 } 431 432 /** 433 * Externalizes the operation's state as a map of attributes. 434 * 435 * <p>A null attribute value is represented by the constant 436 * value {@link jdk.incubator.code.extern.ExternalizedOp#NULL_ATTRIBUTE_VALUE}. 437 * 438 * @return the operation's externalized state, as an unmodifiable map 439 */ 440 public Map<String, Object> externalize() { 441 return Map.of(); 442 } 443 444 /** 445 * Computes values captured by this operation. A captured value is a value that dominates 446 * this operation and is used by a descendant operation. 447 * <p> 448 * The order of the captured values is first use encountered in depth 449 * first search of this operation's descendant operations. 450 * 451 * @return the list of captured values, modifiable 452 * @see Body#capturedValues() 453 */ 454 public List<Value> capturedValues() { 455 Set<Value> cvs = new LinkedHashSet<>(); 456 457 capturedValues(cvs, new ArrayDeque<>(), this); 458 return new ArrayList<>(cvs); 459 } 460 461 static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Op op) { 462 for (Body childBody : op.bodies()) { 463 Body.capturedValues(capturedValues, bodyStack, childBody); 464 } 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 quoted code model of the given quotable reference, if present. 479 * 480 * @param q the quotable reference. 481 * @return the quoted code model or an empty optional if the 482 * quoted code model is unavailable. 483 * @apiNote If the quotable reference is a proxy instance, then the 484 * quoted code model is unavailable and this method 485 * returns an empty optional. 486 * @since 99 487 */ 488 public static Optional<Quoted> ofQuotable(Quotable q) { 489 Object oq = q; 490 if (Proxy.isProxyClass(oq.getClass())) { 491 // @@@ The interpreter implements interpretation of 492 // lambdas using a proxy whose invocation handler 493 // supports the internal protocol to access the quoted instance 494 oq = Proxy.getInvocationHandler(oq); 495 } 496 497 Method method; 498 try { 499 method = oq.getClass().getDeclaredMethod("__internal_quoted"); 500 } catch (NoSuchMethodException e) { 501 return Optional.empty(); 502 } 503 method.setAccessible(true); 504 505 Quoted quoted; 506 try { 507 quoted = (Quoted) method.invoke(oq); 508 } catch (InvocationTargetException | IllegalAccessException e) { 509 throw new RuntimeException(e); 510 } 511 return Optional.of(quoted); 512 } 513 514 /** 515 * Returns the code model of the given method's body, if present. 516 * 517 * @param method the method. 518 * @return the code model of the method body. 519 * @since 99 520 */ 521 // @@@ Make caller sensitive with the same access control as invoke 522 // and throwing IllegalAccessException 523 // @CallerSensitive 524 @SuppressWarnings("unchecked") 525 public static Optional<FuncOp> ofMethod(Method method) { 526 return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess() 527 .setCodeModelIfNeeded(method, Op::createCodeModel); 528 } 529 530 private static Optional<FuncOp> createCodeModel(Method method) { 531 char[] sig = MethodRef.method(method).toString().toCharArray(); 532 for (int i = 0; i < sig.length; i++) { 533 switch (sig[i]) { 534 case '.', ';', '[', '/': sig[i] = '$'; 535 } 536 } 537 String opMethodName = new String(sig); 538 Method opMethod; 539 try { 540 // @@@ Use method handle with full power mode 541 opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName); 542 } catch (NoSuchMethodException e) { 543 return Optional.empty(); 544 } 545 opMethod.setAccessible(true); 546 try { 547 FuncOp funcOp = (FuncOp) opMethod.invoke(null); 548 return Optional.of(funcOp); 549 } catch (ReflectiveOperationException e) { 550 throw new RuntimeException(e); 551 } 552 } 553 554 /** 555 * Returns the code model of provided executable element (if any). 556 * <p> 557 * If the executable element has a code model then it will be an instance of 558 * {@code java.lang.reflect.code.op.CoreOps.FuncOp}. 559 * Note: due to circular dependencies we cannot refer to the type explicitly. 560 * 561 * @implSpec The default implementation unconditionally returns an empty optional. 562 * @param e the executable element. 563 * @return the code model of the provided executable element (if any). 564 * @since 99 565 */ 566 public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) { 567 if (e.getModifiers().contains(Modifier.ABSTRACT) || 568 e.getModifiers().contains(Modifier.NATIVE)) { 569 return Optional.empty(); 570 } 571 572 Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext(); 573 ReflectMethods reflectMethods = ReflectMethods.instance(context); 574 Attr attr = Attr.instance(context); 575 JavacElements elements = JavacElements.instance(context); 576 JavacTrees javacTrees = JavacTrees.instance(context); 577 TreeMaker make = TreeMaker.instance(context); 578 try { 579 JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e); 580 JavacScope scope = javacTrees.getScope(javacTrees.getPath(e)); 581 ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass(); 582 FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree, 583 attribBlock -> reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make)); 584 return Optional.of(op); 585 } catch (RuntimeException ex) { // ReflectMethods.UnsupportedASTException 586 // some other error occurred when attempting to attribute the method 587 // @@@ better report of error 588 ex.printStackTrace(); 589 return Optional.empty(); 590 } 591 } 592 }