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