1 /*
2 * Copyright (c) 2024, 2026, 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.java.JavaOp;
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.invoke.MethodHandle;
49 import java.lang.invoke.MethodType;
50 import java.lang.reflect.Method;
51 import java.lang.reflect.Proxy;
52 import java.util.*;
53 import java.util.function.BiFunction;
54
55 /**
56 * An operation modeling a unit of program behavior.
57 * <p>
58 * An operation uses zero or more values, exposed as a sequence of {@link #operands()}. A
59 * {@link Op.Terminating terminating} operation may have block references, exposed as a sequence of
60 * {@link #successors()}. An operation has zero or more bodies, exposed as a sequence of {@link #bodies()}.
61 *
62 * <h2>Operation construction</h2>
63 * <p>
64 * An operation is constructed by creating an concrete instance of {@link AbstractOp}. Construction creates an
65 * <i>unplaced</i> operation. An unplaced operation is not yet part of a code model. The operation's operands,
66 * successors, bodies, and operation-specific state are fixed when construction completes.
67 * <p>
68 * An operation can only be constructed with operands whose declaring block is being built. Otherwise, construction
69 * fails with an exception.
70 *
71 * <h2>Operation building</h2>
72 * <p>
73 * Building an operation places an unplaced operation in a code model in one of two ways:
74 * <ol>
75 * <li>
76 * the operation is <i>placed</i> in a block, which becomes its parent block, by using a block builder to
77 * {@link Block.Builder#add(Op) append} the operation to the block. The placed operation has a permanently
78 * non-{@code null} {@link #result() result} that can be used as an operand of subsequently constructed operations. The
79 * block being built is not <a href="Body.Builder.html#body-building-observability">observable</a> through this
80 * operation and any attempt to access the block throws {@link IllegalStateException}.
81 * <li>
82 * the operation is <i>placed</i> as the {@link #isRoot() <i>root operation</i>} of a code model by using
83 * {@link #buildAsRoot()}. The root operation's {@link #result() result} and {@link #parent() parent} are always
84 * {@code null}.
85 * </ol>
86 * <p>
87 * Building finishes when the parent body builder of the block in which the operation was placed
88 * <a href="Body.Builder.html#body-building-finishing">finishes</a>, after which the block becomes observable, or when
89 * the operation is placed as the root of a code model. After building finishes, the operation's placement and location
90 * are also fixed, and from then on the operation's observable state does not change.
91 * <p>
92 * The {@link #location} may be {@link #setLocation set} while the operation is unplaced or placed in a block whose
93 * parent body builder has not finished.
94 * <p>
95 * An unplaced operation, or an operation placed in a block whose parent body builder has not
96 * <a href="Body.Builder.html#body-building-finishing">finished</a>, is not thread-safe.
97 *
98 * @apiNote
99 * An operation might model the {@link JavaOp.AddOp addition} of two integers, or a method
100 * {@link JavaOp.InvokeOp invocation} expression. Alternatively an operation may model something more complex like
101 * {@link jdk.incubator.code.dialect.core.CoreOp.FuncOp method} declarations, {@link JavaOp.LambdaOp lambda}
102 * expressions, or {@link JavaOp.TryOp try} statements. In such cases an operation will contain one or more bodies
103 * modeling the nested structure.
104 */
105 public sealed interface Op extends CodeElement<Op, Body> permits Op.Terminating, InternalAbstractOp {
106
107 /**
108 * A terminating operation that occurs as the last operation in a block.
109 * <p>
110 * A terminating operation passes control to either another block within the same parent body
111 * or to that parent body.
112 * <p>
113 * A terminating operation is a body terminating operation if it is guaranteed to always have zero successors.
114 * A terminating operation is a block terminating operation if it is guaranteed to always have one or more
115 * successors.
116 */
117 public sealed interface Terminating extends Op permits AbstractOp.Terminating {
118 }
119
120 /**
121 * An operation characteristic indicating the operation has one or more bodies.
122 */
123 public interface Nested {
124 /**
125 * {@return a non-empty list of this nested operation's bodies.}
126 */
127 List<Body> bodies();
128 }
129
130 /**
131 * An operation characteristic indicating the operation represents a loop.
132 */
133 public interface Loop extends Nested {
134 /**
135 * {@return the body of this loop operation.}
136 * <p>
137 * The returned body is one of this operation's {@link #bodies() bodies}.
138 */
139 Body loopBody();
140 }
141
142 /**
143 * An operation characteristic indicating the operation has one or more bodies,
144 * all of which are isolated and capture no values.
145 */
146 public interface Isolated extends Nested {
147 }
148
149 /**
150 * An operation characteristic indicating the operation is invokable.
151 */
152 public interface Invokable extends Nested {
153 /**
154 * {@return the body of this invokable operation.}
155 * <p>
156 * The returned body is one of this operation's {@linkplain #bodies() bodies}.
157 */
158 Body body();
159
160 /**
161 * {@return the invokable operation's signature, represented as a function type.}
162 * @implSpec
163 * The default implementation returns the signature of the invokable operation's body.
164 */
165 default FunctionType invokableSignature() {
166 return body().bodySignature();
167 }
168
169 /**
170 * {@return the entry block parameters of this operation's body}
171 * @implSpec
172 * The default implementation returns the entry block's parameters of the invokable operation's body.
173 */
174 default List<Block.Parameter> parameters() {
175 return body().entryBlock().parameters();
176 }
177
178 /**
179 * Computes values captured by this invokable operation's body.
180 *
181 * @return the captured values.
182 * @see Body#capturedValues()
183 */
184 List<Value> capturedValues();
185 }
186
187 /**
188 * An operation characteristic indicating the operation can lower itself by replacing itself with blocks and
189 * operations that represent the same behavior.
190 */
191 // @@@ Hide this abstraction within JavaOp?
192 public interface Lowerable {
193
194 /**
195 * Lowers this operation into the given block builder.
196 * <p>
197 * A lowering implementation emits the replacement blocks and operations into the given builder, and returns
198 * the block builder to use for subsequent operations in an enclosing transformation.
199 * <p>
200 * If this operation lowers one of its bodies, it should transform that body with a lowering code transformer
201 * produced by {@link #loweringTransformer(BiFunction, BiFunction)}. This ensures that lowerable operations
202 * encountered in that body are lowered recursively.
203 * The {@code inherited} transformer is the operation transformer inherited from an enclosing lowering, if any.
204 * A lowering implementation may pass it directly to {@code loweringTransformer}, or compose it with another
205 * transformer and pass the composed transformer. The transformer passed to {@code loweringTransformer} is then
206 * supplied as the inherited transformer when that lowering code transformer recursively lowers lowerable
207 * operations.
208 *
209 * @param b the block builder into which this operation is lowered
210 * @param inherited the inherited operation transformer, may be {@code null}
211 * @return the block builder to use for subsequent building
212 */
213 Block.Builder lower(Block.Builder b, BiFunction<Block.Builder, Op, Block.Builder> inherited);
214
215 /**
216 * Returns a lowering code transformer that partially composes the given operation transformers and, if
217 * required, lowers lowerable operations and appends non-lowerable operations.
218 * <p>
219 * The returned code transformer accepts an operation by first applying the partial composition of
220 * {@code current} with {@code inherited} in the first argument of {@code current}, as if by the following:
221 * {@snippet lang = "java":
222 * Block.Builder composedBlock = inherited == null
223 * ? block
224 * : inherited.apply(block, op);
225 * Block.Builder currentBlock = current.apply(composedBlock, op);
226 * }
227 * The returned continuation builder is then selected as if by the following:
228 * {@snippet lang = "java":
229 * if (currentBlock != null) {
230 * return currentBlock;
231 * } else if (op instanceof Op.Lowerable lop) {
232 * return lop.lower(composedBlock, inherited);
233 * } else {
234 * composedBlock.op(op);
235 * return composedBlock;
236 * }
237 * }
238 *
239 * @param inherited the inherited operation transformer, may be {@code null}
240 * @param current the current operation transformer
241 * @return the lowering code transformer
242 */
243 static CodeTransformer loweringTransformer(BiFunction<Block.Builder, Op, Block.Builder> inherited,
244 BiFunction<Block.Builder, Op, Block.Builder> current) {
245 Objects.requireNonNull(current);
246 return (block, op) -> {
247 if (inherited != null) {
248 block = inherited.apply(block, op);
249 }
250 Block.Builder currentBlock = current.apply(block, op);
251 if (currentBlock != null) {
252 return currentBlock;
253 } else if (op instanceof Op.Lowerable lop) {
254 return lop.lower(block, inherited);
255 } else {
256 block.add(op);
257 return block;
258 }
259 };
260 }
261 }
262
263 /**
264 * A value that is the result of an operation.
265 */
266 public static final class Result extends Value {
267
268 /**
269 * If assigned to an operation's result field, indicates the operation is a root operation.
270 */
271 static final Result ROOT_RESULT = new Result();
272
273 final Op op;
274
275 private Result() {
276 // Constructor for instance of ROOT_RESULT
277 super(null, null);
278 this.op = null;
279 }
280
281 Result(Block block, Op op) {
282 super(block, op.resultType());
283
284 this.op = op;
285 }
286
287 @Override
288 public String toString() {
289 return "%result@" + Integer.toHexString(hashCode());
290 }
291
292 @Override
293 public SequencedSet<Value> dependsOn() {
294 SequencedSet<Value> depends = new LinkedHashSet<>(op.operands());
295 if (op instanceof Terminating) {
296 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
297 }
298
299 return Collections.unmodifiableSequencedSet(depends);
300 }
301
302 /**
303 * {@return the result's declaring operation.}
304 */
305 public Op op() {
306 return op;
307 }
308 }
309
310 /**
311 * Source location information for an operation.
312 *
313 * @param sourceRef the reference to the source, {@code null} if absent
314 * @param line the line in the source
315 * @param column the column in the source
316 */
317 public record Location(String sourceRef, int line, int column) {
318
319 /**
320 * The location value, {@code null}, indicating no location information.
321 */
322 public static final Location NO_LOCATION = null;
323
324 /**
325 * Constructs a location with line and column only.
326 *
327 * @param line the line in the source
328 * @param column the column in the source
329 */
330 public Location(int line, int column) {
331 this(null, line, column);
332 }
333 }
334
335
336 /**
337 * Transforms this operation, copying the operation and transforming any of its bodies.
338 * <p>
339 * This method returns a newly constructed, unplaced copy of this operation. The returned operation's concrete
340 * class is the same as this operation's concrete class.
341 * <p>
342 * The returned operation copies this operation's operands, successors, and any operation-specific state. Operands
343 * are copied by mapping this operation's operands, in order, with the given code context. Successors are copied as
344 * specified by {@link CodeContext#getReferenceOrCreate(Block.Reference)}. Operation-specific state is copied as
345 * appropriate for the operation, preserving operation-specific behavior.
346 * <p>
347 * Bodies are {@link Body#transform(CodeContext, CodeTransformer) transformed} with the given code context and code
348 * transformer, and built with the returned operation as their parent.
349 *
350 * @apiNote
351 * To copy an operation use the {@link CodeTransformer#COPYING_TRANSFORMER copying transformer}.
352 *
353 * @param cc the code context
354 * @param ct the code transformer
355 * @return the transformed operation
356 * @see CodeTransformer#COPYING_TRANSFORMER
357 */
358 public Op transform(CodeContext cc, CodeTransformer ct);
359
360 /**
361 * Sets the originating source location of this operation.
362 *
363 * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
364 * @throws IllegalStateException if this operation is a root operation, or is placed in a built block.
365 */
366 public void setLocation(Location l);
367
368 /**
369 * {@return the originating source location of this operation, otherwise {@code null} if not specified}
370 */
371 public Location location();
372
373 /**
374 * Returns this operation's parent block, or {@code null} if this operation is unplaced or a root operation.
375 * <p>
376 * The operation's parent block is the same as the operation result's {@link Value#declaringBlock declaring block}.
377 *
378 * @return operation's parent block, or {@code null} if this operation is unplaced or a root operation.
379 * @throws IllegalStateException if this operation is placed in a block that is
380 * <a href="Body.Builder.html#body-building-observability">unobservable</a>.
381 * @see Value#declaringBlock()
382 */
383 @Override
384 public Block parent();
385
386 /**
387 * {@return the operation's bodies, as an unmodifiable list}
388 * @see #children()
389 */
390 public List<Body> bodies();
391
392 /**
393 * {@return the operation's result type}
394 */
395 public CodeType resultType();
396
397 /**
398 * {@return the operation's result, or {@code null} if this operation is unplaced or a
399 * root operation.}
400 */
401 public Result result();
402
403 /**
404 * {@return the operation's operands, as an unmodifiable list}
405 */
406 public List<Value> operands();
407
408 /**
409 * {@return the operation's successors, as an unmodifiable list}
410 * <p>
411 * If this operation is not an instance of {@link Op.Terminating} then the operation has zero successors
412 * and this method returns an empty unmodifiable list. Otherwise, if this operation is an instance of
413 * {@code Op.Terminating}, this method may return zero or more successors.
414 *
415 * @see Op.Terminating
416 */
417 // @@@ Move to Op.Terminating?
418 public List<Block.Reference> successors();
419
420 /**
421 * Returns the operation's signature, represented as a function type.
422 * <p>
423 * The signature's return type is the operation's result type and its parameter types are the
424 * operation's operand types, in order.
425 *
426 * @return the operation's signature
427 */
428 public FunctionType opSignature();
429
430 /**
431 * Computes values captured by this operation. A captured value is a value that is used but not declared by any
432 * descendant operation of this operation.
433 * <p>
434 * The order of the captured values is first use encountered in depth-first search of this operation's descendant
435 * operations.
436 *
437 * @return the list of captured values, modifiable
438 * @see Body#capturedValues()
439 */
440 public List<Value> capturedValues();
441
442 /**
443 * Builds this operation, placing it as the root operation of a code model. After this operation is placed as a root
444 * operation, its {@link #result() result} and {@link #parent() parent} will always be {@code null}.
445 * <p>
446 * This method is idempotent.
447 *
448 * @throws IllegalStateException if this operation is placed in a block, has any successors,
449 * uses any values as operands, or any of its bodies is open.
450 * @see #isRoot()
451 * @see Body#isIsolated
452 */
453 public void buildAsRoot();
454
455 /**
456 * {@return {@code true} if this operation is a root operation.}
457 * @see #buildAsRoot()
458 * @see #isPlacedInBlock()
459 */
460 public boolean isRoot();
461
462 /**
463 * {@return {@code true} if this operation is placed in a block.}
464 * @see #buildAsRoot()
465 * @see #isRoot()
466 */
467 public boolean isPlacedInBlock();
468
469 /**
470 * Returns the code model text for this operation.
471 * <p>
472 * The format of code model text is unspecified.
473 *
474 * @return the code model text for this operation.
475 * @apiNote Code model text is designed to be human-readable and is intended for debugging, testing,
476 * and comprehension.
477 * @see OpWriter#toText(Op, OpWriter.Option...)
478 */
479 public String toText();
480
481
482 /**
483 * Returns a quoted instance containing the code model of a reflectable lambda expression or method reference.
484 * <p>
485 * The quoted instance also contains a mapping from {@link Value values} in the code model that model final, or
486 * effectively final, variables used but not declared in the lambda expression to their corresponding run time
487 * values. Such run time values are commonly referred to as captured values.
488 * <p>
489 * Repeated invocations of this method will return a quoted instance containing the same instance of the code model.
490 * Therefore, code elements (and more generally code items) contained within the code model can be reliably compared
491 * using object identity.
492 *
493 * @param fiInstance a functional interface instance that is the result of a reflectable lambda expression or
494 * method reference.
495 * @return the quoted instance containing the code model, or an empty optional if the functional interface instance
496 * is not the result of a reflectable lambda expression or method reference.
497 * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
498 * model is not the same as the Java version used at runtime to load the code model.
499 * @apiNote if the functional interface instance is a proxy instance, then the quoted code model is unavailable and
500 * this method returns an empty optional.
501 */
502 public static Optional<Quoted<JavaOp.LambdaOp>> ofLambda(Object fiInstance) {
503 Object oq = fiInstance;
504 if (Proxy.isProxyClass(oq.getClass())) {
505 // @@@ The interpreter implements interpretation of
506 // lambdas using a proxy whose invocation handler
507 // supports the internal protocol to access the quoted instance
508 oq = Proxy.getInvocationHandler(oq);
509 }
510 MethodHandle mh;
511 try {
512 mh = SharedSecrets.getJavaLangInvokeAccess().findVirtual(oq.getClass(), "__internal_quoted",
513 MethodType.methodType(Quoted.class));
514 } catch (IllegalAccessException e) { // @@@ when this is thrown and what to do about it ?
515 throw new RuntimeException(e);
516 }
517 if (mh == null) {
518 return Optional.empty();
519 }
520 Quoted<?> q;
521 try {
522 q = (Quoted<?>) mh.invoke(oq);
523 } catch (Throwable e) {
524 // @@@ revisit this
525 // op method may throw UOE in case java compile time version doesn't match runtime version
526 if (e instanceof RuntimeException re) {
527 throw re;
528 }
529 throw new RuntimeException(e);
530 }
531 if (!(q.op() instanceof JavaOp.LambdaOp)) {
532 // This can only happen if the stored model is invalid
533 throw new RuntimeException("Invalid code model for lambda expression : " + q);
534 }
535 @SuppressWarnings("unchecked")
536 Quoted<JavaOp.LambdaOp> lq = (Quoted<JavaOp.LambdaOp>) q;
537 return Optional.of(lq);
538 }
539
540 /**
541 * Returns the code model of a reflectable method.
542 * <p>
543 * Repeated invocations of this method will return the same instance of the code model. Therefore,
544 * code elements (and more generally code items) contained within the code model can be reliably compared using
545 * object identity.
546 *
547 * @param method the method.
548 * @return the code model, or an empty optional if the method is not reflectable.
549 * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
550 * model is not the same as the Java version used at runtime to load the code model.
551 */
552 // @@@ Make caller sensitive with the same access control as invoke
553 // and throwing IllegalAccessException
554 // @CallerSensitive
555 @SuppressWarnings("unchecked")
556 public static Optional<FuncOp> ofMethod(Method method) {
557 return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess()
558 .setCodeModelIfNeeded(method, Op::createCodeModel);
559 }
560
561 private static Optional<FuncOp> createCodeModel(Method method) {
562 char[] sig = MethodRef.method(method).toString().toCharArray();
563 for (int i = 0; i < sig.length; i++) {
564 switch (sig[i]) {
565 case '.', ';', '[', '/': sig[i] = '$';
566 }
567 }
568 String opMethodName = new String(sig);
569 MethodHandle mh;
570 try {
571 mh = SharedSecrets.getJavaLangInvokeAccess().findStatic(method.getDeclaringClass(), opMethodName,
572 MethodType.methodType(Op.class));
573 } catch (IllegalAccessException e) { // @@@ when this is thrown and what to do about it ?
574 throw new RuntimeException(e);
575 }
576 if (mh == null) {
577 return Optional.empty();
578 }
579 try {
580 FuncOp funcOp = (FuncOp) mh.invoke();
581 return Optional.of(funcOp);
582 } catch (Throwable e) {
583 // @@@ revisit this
584 // op method may throw UOE in case java compile time version doesn't match runtime version
585 if (e instanceof RuntimeException re) {
586 throw re;
587 }
588 throw new RuntimeException(e);
589 }
590 }
591
592 /**
593 * Returns the code model of an executable element.
594 * <p>
595 * Repeated invocations of this method will return distinct instances of the code model.
596 *
597 * @param processingEnvironment the annotation processing environment
598 * @param e the executable element.
599 * @return the code model, or an empty optional if the executable element is not reflectable.
600 */
601 public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
602 if (e.getModifiers().contains(Modifier.ABSTRACT) ||
603 e.getModifiers().contains(Modifier.NATIVE)) {
604 return Optional.empty();
605 }
606
607 Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
608 ReflectMethods reflectMethods = ReflectMethods.instance(context);
609 Attr attr = Attr.instance(context);
610 JavacElements elements = JavacElements.instance(context);
611 JavacTrees javacTrees = JavacTrees.instance(context);
612 TreeMaker make = TreeMaker.instance(context);
613 try {
614 JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
615 JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
616 ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
617 FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
618 attribBlock -> {
619 try {
620 return reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make);
621 } catch (Throwable ex) {
622 // this might happen if the source code contains errors
623 return null;
624 }
625 });
626 return Optional.ofNullable(op);
627 } catch (RuntimeException ex) { // ReflectMethods.UnsupportedASTException
628 // some other error occurred when attempting to attribute the method
629 // @@@ better report of error
630 ex.printStackTrace();
631 return Optional.empty();
632 }
633 }
634 }