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 java.lang.reflect.code; 27 28 import java.io.OutputStream; 29 import java.io.OutputStreamWriter; 30 import java.io.StringWriter; 31 import java.io.Writer; 32 import java.lang.reflect.code.type.FunctionType; 33 import java.lang.reflect.code.writer.OpWriter; 34 import java.nio.charset.StandardCharsets; 35 import java.util.*; 36 import java.util.function.BiFunction; 37 38 /** 39 * An operation modelling a unit of functionality. 40 * <p> 41 * An operation might model the addition of two 32-bit integers, or a Java method call. 42 * Alternatively an operation may model something more complex like method bodies, lambda bodies, or 43 * try/catch/finally statements. In this case such an operation will contain one or more bodies modelling 44 * the nested structure. 45 */ 46 public non-sealed abstract class Op implements CodeElement<Op, Body> { 47 48 /** 49 * An operation characteristic indicating the operation is pure and has no side effects. 50 */ 51 public interface Pure { 52 } 53 54 /** 55 * An operation characteristic indicating the operation has one or more bodies. 56 */ 57 public interface Nested { 58 List<Body> bodies(); 59 } 60 61 /** 62 * An operation characteristic indicating the operation represents a loop 63 */ 64 public interface Loop extends Nested { 65 Body loopBody(); 66 } 67 68 /** 69 * An operation characteristic indicating the operation has one or more bodies, 70 * all of which are isolated. 71 */ 72 public interface Isolated extends Nested { 73 } 74 75 /** 76 * An operation characteristic indicating the operation is invokable, so the operation may be interpreted 77 * or compiled. 78 */ 79 public interface Invokable extends Nested { 80 /** 81 * {@return the body of the invokable operation.} 82 */ 83 Body body(); 84 85 /** 86 * {@return the function type describing the invokable operation's parameter types and return type.} 87 */ 88 FunctionType invokableType(); 89 90 /** 91 * {@return the entry block parameters of this operation's body} 92 */ 93 default List<Block.Parameter> parameters() { 94 return body().entryBlock().parameters(); 95 } 96 97 /** 98 * Computes values captured by this invokable operation's body. 99 * 100 * @return the captured values. 101 * @see Body#capturedValues() 102 */ 103 default List<Value> capturedValues() { 104 return List.of(); 105 } 106 } 107 108 /** 109 * An operation characteristic indicating the operation can replace itself with a lowered form, 110 * consisting only of operations in the core dialect. 111 */ 112 public interface Lowerable { 113 default Block.Builder lower(Block.Builder b) { 114 return lower(b, OpTransformer.NOOP_TRANSFORMER); 115 } 116 117 Block.Builder lower(Block.Builder b, OpTransformer opT); 118 } 119 120 /** 121 * An operation characteristic indicating the operation is a terminating operation 122 * occurring as the last operation in a block. 123 * <p> 124 * A terminating operation passes control to either another block within the same parent body 125 * or to that parent body. 126 */ 127 public interface Terminating { 128 } 129 130 /** 131 * An operation characteristic indicating the operation is a body terminating operation 132 * occurring as the last operation in a block. 133 * <p> 134 * A body terminating operation passes control back to its nearest ancestor body. 135 */ 136 public interface BodyTerminating extends Terminating { 137 } 138 139 /** 140 * An operation characteristic indicating the operation is a block terminating operation 141 * occurring as the last operation in a block. 142 * <p> 143 * The operation has one or more successors to other blocks within the same parent body, and passes 144 * control to one of those blocks. 145 */ 146 public interface BlockTerminating extends Terminating { 147 List<Block.Reference> successors(); 148 } 149 150 /** 151 * A value that is the result of an operation. 152 */ 153 public static final class Result extends Value { 154 final Op op; 155 156 Result(Block block, Op op) { 157 super(block, op.resultType()); 158 159 this.op = op; 160 } 161 162 @Override 163 public Set<Value> dependsOn() { 164 Set<Value> depends = new LinkedHashSet<>(op.operands()); 165 if (op instanceof Terminating) { 166 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add); 167 } 168 169 return Collections.unmodifiableSet(depends); 170 } 171 172 /** 173 * Returns the result's operation. 174 * 175 * @return the result's operation. 176 */ 177 public Op op() { 178 return op; 179 } 180 } 181 182 // Set when op is bound to block, otherwise null when unbound 183 Result result; 184 185 // null if not specified 186 Location location; 187 188 final String name; 189 190 final List<Value> operands; 191 192 /** 193 * Constructs an operation by copying given operation. 194 * 195 * @param that the operation to copy. 196 * @param cc the copy context. 197 * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list 198 * values computed, in order, by mapping the operation's operands using the copy context. 199 */ 200 protected Op(Op that, CopyContext cc) { 201 this(that.name, cc.getValues(that.operands)); 202 this.location = that.location; 203 } 204 205 /** 206 * Copies this operation and its bodies, if any. 207 * <p> 208 * The returned operation is structurally identical to this operation and is otherwise independent 209 * of the values declared and used. 210 * 211 * @return the copied operation. 212 */ 213 public Op copy() { 214 return transform(CopyContext.create(), OpTransformer.COPYING_TRANSFORMER); 215 } 216 217 /** 218 * Copies this operation and its bodies, if any. 219 * <p> 220 * The returned operation is structurally identical to this operation and is otherwise independent 221 * of the values declared and used. 222 * 223 * @param cc the copy context. 224 * @return the copied operation. 225 */ 226 public Op copy(CopyContext cc) { 227 return transform(cc, OpTransformer.COPYING_TRANSFORMER); 228 } 229 230 /** 231 * Copies this operation and transforms its bodies, if any. 232 * <p> 233 * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and 234 * operation transformer. 235 * 236 * @param cc the copy context. 237 * @param ot the operation transformer. 238 * @return the transformed operation. 239 */ 240 public abstract Op transform(CopyContext cc, OpTransformer ot); 241 242 /** 243 * Constructs an operation with a name and list of operands. 244 * 245 * @param name the operation name. 246 * @param operands the list of operands, a copy of the list is performed if required. 247 */ 248 protected Op(String name, List<? extends Value> operands) { 249 this.name = name; 250 this.operands = List.copyOf(operands); 251 } 252 253 /** 254 * Sets the originating source location of this operation, if unbound. 255 * 256 * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified. 257 * @throws IllegalStateException if this operation is bound 258 */ 259 public final void setLocation(Location l) { 260 // @@@ Fail if location != null? 261 if (result != null && result.block.isBound()) { 262 throw new IllegalStateException(); 263 } 264 265 location = l; 266 } 267 268 /** 269 * {@return the originating source location of this operation, otherwise {@code null} if not specified} 270 */ 271 public final Location location() { 272 return location; 273 } 274 275 /** 276 * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block. 277 * 278 * @return operation's parent block, or {@code null} if the operation is not assigned to a block. 279 */ 280 @Override 281 public final Block parent() { 282 return parentBlock(); 283 } 284 285 /** 286 * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block. 287 * 288 * @return operation's parent block, or {@code null} if the operation is not assigned to a block. 289 */ 290 public final Block parentBlock() { 291 if (result == null) { 292 return null; 293 } 294 295 if (!result.block.isBound()) { 296 throw new IllegalStateException("Parent block is partially constructed"); 297 } 298 299 return result.block; 300 } 301 302 @Override 303 public final List<Body> children() { 304 return bodies(); 305 } 306 307 /** 308 * {@return the operation's bodies, as an unmodifiable list} 309 * @implSpec this implementation returns an unmodifiable empty list. 310 */ 311 public List<Body> bodies() { 312 return List.of(); 313 } 314 315 /** 316 * Returns the operation's result, otherwise {@code null} if the operation is not assigned to a block. 317 * 318 * @return the operation's result, or {@code null} if not assigned to a block. 319 */ 320 public final Result result() { 321 return result; 322 } 323 324 325 /** 326 * Returns this operation's nearest ancestor body (the parent body of this operation's parent block), 327 * otherwise {@code null} if the operation is not assigned to a block. 328 * 329 * @return operation's nearest ancestor body, or {@code null} if the operation is not assigned to a block. 330 */ 331 public final Body ancestorBody() { 332 if (result == null) { 333 return null; 334 } 335 336 if (!result.block.isBound()) { 337 throw new IllegalStateException("Parent body is partially constructed"); 338 } 339 340 return result.block.parentBody; 341 } 342 343 /** 344 * {@return the operation name} 345 */ 346 public String opName() { 347 return name; 348 } 349 350 /** 351 * {@return the operation's operands, as an unmodifiable list} 352 */ 353 public List<Value> operands() { 354 return operands; 355 } 356 357 /** 358 * {@return the operation's successors, as an unmodifiable list} 359 */ 360 public List<Block.Reference> successors() { 361 return List.of(); 362 } 363 364 /** 365 * {@return the operation's result type} 366 */ 367 public abstract TypeElement resultType(); 368 369 /** 370 * Returns the operation's function type. 371 * <p> 372 * The function type's result type is the operation's result type and the function type's parameter types are the 373 * operation's operand types, in order. 374 * 375 * @return the function type 376 */ 377 public FunctionType opType() { 378 List<TypeElement> operandTypes = operands.stream().map(Value::type).toList(); 379 return FunctionType.functionType(resultType(), operandTypes); 380 } 381 382 /** 383 * Traverse the operands of this operation that are the results of prior operations, recursively. 384 * <p> 385 * Traversal is performed in pre-order, reporting the operation of each operand to the visitor. 386 * 387 * @param t the traversing accumulator 388 * @param v the visitor 389 * @param <T> accumulator type 390 * @return the traversing accumulator 391 * @apiNote A visitor that implements the abstract method of {@code OpVisitor} and does not override any 392 * other default method will only visit operations. As such a lambda expression or method reference 393 * may be used to visit operations. 394 */ 395 public final <T> T traverseOperands(T t, BiFunction<T, Op, T> v) { 396 for (Value arg : operands()) { 397 if (arg instanceof Result or) { 398 t = v.apply(t, or.op); 399 t = or.op.traverseOperands(t, v); 400 } 401 } 402 403 return t; 404 } 405 406 /** 407 * Computes values captured by this operation. A captured value is a value that dominates 408 * this operation and is used by a descendant operation. 409 * <p> 410 * The order of the captured values is first use encountered in depth 411 * first search of this operation's descendant operations. 412 * 413 * @return the list of captured values, modifiable 414 * @see Body#capturedValues() 415 */ 416 public List<Value> capturedValues() { 417 Set<Value> cvs = new LinkedHashSet<>(); 418 419 capturedValues(cvs, new ArrayDeque<>(), this); 420 return new ArrayList<>(cvs); 421 } 422 423 static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Op op) { 424 for (Body childBody : op.bodies()) { 425 Body.capturedValues(capturedValues, bodyStack, childBody); 426 } 427 } 428 429 /** 430 * Writes the textual form of this operation to the given output stream, using the UTF-8 character set. 431 * 432 * @param out the stream to write to. 433 */ 434 public void writeTo(OutputStream out) { 435 writeTo(new OutputStreamWriter(out, StandardCharsets.UTF_8)); 436 } 437 438 /** 439 * Writes the textual form of this operation to the given writer. 440 * 441 * @param w the writer to write to. 442 */ 443 public void writeTo(Writer w) { 444 OpWriter.writeTo(w, this); 445 } 446 447 /** 448 * Returns the textual form of this operation. 449 * 450 * @return the textual form of this operation. 451 */ 452 public String toText() { 453 return OpWriter.toText(this); 454 } 455 }