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