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