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