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