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