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