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