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 modelling a unit of program behaviour.
56 * <p>
57 * An operation might model the addition of two integers, or a method invocation expression.
58 * Alternatively an operation may model something more complex like method declarations, lambda expressions, or
59 * try statements. In such cases an operation will contain one or more bodies modelling the nested structure.
60 * <p>
61 * An instance of an operation when initially constructed is referred to as an unbuilt operation.
62 * An unbuilt operation's state and descendants are all immutable except for its {@link #result result} and
63 * {@link #parent parent}, which are initially set to {@code null}.
64 * <p>
65 * An unbuilt operation transitions to a built operation in one of two ways:
66 * <ol>
67 * <li>
68 * {@link #buildAsRoot() building} the unbuilt operation to become a built {@link #isRoot() root} operation. The
69 * operation's {@link #result result} and {@link #parent parent} are always {@code null}.
70 * </li>
71 * <li>
72 * {@link Block.Builder#op(Op) appending} the unbuilt operation to a block builder to first become an unbuilt-bound
73 * operation that is bound to an operation result and parent block.
74 * The unbuilt-bound operation has a non-{@code null} unbuilt {@link #result result} that never changes, an unbuilt
75 * value that can be used by subsequent constructed operations.
76 * An unbuilt-bound operation transitions to a built bound operation when the block builder it was appended to builds
77 * the block, after which the built bound operation has a non-{@code null} {@link #parent parent} that never changes and
78 * a built {@link #result}.
79 * Before then the unbuilt-bound operation's {@link #parent parent} is inaccessible, as is unbuilt result's
80 * {@link Value#declaringBlock() declaring block}) (since both refer to the same block).
81 * </li>
82 * </ol>
83 * A built operation is fully immutable either as a root operation, the root of a code model, or as a bound operation
84 * within a code model.
85 * <p>
86 * An operation can only be constructed with unbuilt values as operands (if any) and unbuilt block references as
87 * successors (if any), otherwise construction fails with an exception.
88 */
89 public non-sealed abstract class Op implements CodeElement<Op, Body> {
90
91 /**
92 * An operation characteristic indicating the operation is pure and has no side effects.
93 */
94 public interface Pure {
95 }
96
97 /**
98 * An operation characteristic indicating the operation has one or more bodies.
99 */
100 public interface Nested {
101 /**
102 * {@return the bodies of the nested operation.}
103 */
104 List<Body> bodies();
105 }
106
107 /**
108 * An operation characteristic indicating the operation represents a loop
109 */
110 public interface Loop extends Nested {
111 /**
112 * {@return the body of the loop operation.}
113 */
114 Body loopBody();
115 }
116
117 /**
118 * An operation characteristic indicating the operation has one or more bodies,
119 * all of which are isolated and capture no values.
120 */
121 public interface Isolated extends Nested {
122 }
123
124 /**
125 * An operation characteristic indicating the operation is invokable.
126 */
127 public interface Invokable extends Nested {
128 /**
129 * {@return the body of the invokable operation.}
130 */
131 Body body();
132
133 /**
134 * {@return the function type describing the invokable operation's parameter types and return type.}
135 */
136 FunctionType invokableType();
137
138 /**
139 * {@return the entry block parameters of this operation's body}
140 */
141 default List<Block.Parameter> parameters() {
142 return body().entryBlock().parameters();
143 }
144
145 /**
146 * Computes values captured by this invokable operation's body.
147 *
148 * @return the captured values.
149 * @see Body#capturedValues()
150 */
151 default List<Value> capturedValues() {
152 return List.of();
153 }
154 }
155
156 /**
157 * An operation characteristic indicating the operation can replace itself with a lowered form.
158 */
159 // @@@ Hide this abstraction within JavaOp?
160 public interface Lowerable {
161
162 /**
163 * Lowers this operation into the block builder, commonly replacing nested structure
164 * with interconnected basic blocks. The previous lowering code transformation
165 * is used to compose with a lowering transformation that is applied to bodies
166 * of this operation, ensuring lowering is applied consistently to nested content.
167 *
168 * @param b the block builder
169 * @param opT the previous lowering code transformation, may be {@code null}
170 * @return the block builder to use for further building
171 */
172 Block.Builder lower(Block.Builder b, CodeTransformer opT);
173
174 /**
175 * Returns a composed code transformer that composes with an operation transformer function adapted to lower
176 * operations.
177 * <p>
178 * This method behaves as if it returns the result of the following expression:
179 * {@snippet lang = java:
180 * CodeTransformer.andThen(before, lowering(before, f));
181 *}
182 *
183 * @param before the code transformer to apply before
184 * @param f the operation transformer function to apply after
185 * @return the composed code transformer
186 */
187 static CodeTransformer andThenLowering(CodeTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) {
188 return CodeTransformer.andThen(before, lowering(before, f));
189 }
190
191 /**
192 * Returns an adapted operation transformer function that adapts an operation transformer function
193 * {@code f} to also transform lowerable operations.
194 * <p>
195 * The adapted operation transformer function first applies a block builder and operation
196 * to the operation transformer function {@code f}.
197 * If the result is not {@code null} then the result is returned.
198 * Otherwise, if the operation is a lowerable operation then the result of applying the
199 * block builder and code transformer {@code before} to {@link Lowerable#lower lower}
200 * of the lowerable operation is returned.
201 * Otherwise, the operation is copied by applying it to {@link Block.Builder#op op} of the block builder,
202 * and the block builder is returned.
203 *
204 * @param before the code transformer to apply for lowering
205 * @param f the operation transformer function to apply after
206 * @return the adapted operation transformer function
207 */
208 static BiFunction<Block.Builder, Op, Block.Builder> lowering(CodeTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) {
209 return (block, op) -> {
210 Block.Builder b = f.apply(block, op);
211 if (b == null) {
212 if (op instanceof Lowerable lop) {
213 block = lop.lower(block, before);
214 } else {
215 block.op(op);
216 }
217 } else {
218 block = b;
219 }
220 return block;
221 };
222 }
223 }
224
225 /**
226 * An operation characteristic indicating the operation is a terminating operation
227 * occurring as the last operation in a block.
228 * <p>
229 * A terminating operation passes control to either another block within the same parent body
230 * or to that parent body.
231 */
232 public interface Terminating {
233 }
234
235 /**
236 * An operation characteristic indicating the operation is a body terminating operation
237 * occurring as the last operation in a block.
238 * <p>
239 * A body terminating operation passes control back to its nearest ancestor body.
240 */
241 public interface BodyTerminating extends Terminating {
242 }
243
244 /**
245 * An operation characteristic indicating the operation is a block terminating operation
246 * occurring as the last operation in a block.
247 * <p>
248 * The operation has one or more successors to other blocks within the same parent body, and passes
249 * control to one of those blocks.
250 */
251 public interface BlockTerminating extends Terminating {
252 /**
253 * {@return the list of successor blocks associated with this block terminating operation}
254 */
255 List<Block.Reference> successors();
256 }
257
258 /**
259 * A value that is the result of an operation.
260 */
261 public static final class Result extends Value {
262
263 /**
264 * If assigned to an operation result, it indicates the operation is a root operation
265 */
266 private static final Result ROOT_RESULT = new Result();
267
268 final Op op;
269
270 private Result() {
271 // Constructor for instance of ROOT_RESULT
272 super(null, null);
273 this.op = null;
274 }
275
276 Result(Block block, Op op) {
277 super(block, op.resultType());
278
279 this.op = op;
280 }
281
282 @Override
283 public String toString() {
284 return "%result@" + Integer.toHexString(hashCode());
285 }
286
287 @Override
288 public SequencedSet<Value> dependsOn() {
289 SequencedSet<Value> depends = new LinkedHashSet<>(op.operands());
290 if (op instanceof Terminating) {
291 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
292 }
293
294 return Collections.unmodifiableSequencedSet(depends);
295 }
296
297 /**
298 * {@return the result's declaring operation.}
299 */
300 public Op op() {
301 return op;
302 }
303 }
304
305 /**
306 * Source location information for an operation.
307 *
308 * @param sourceRef the reference to the source, {@code null} if absent
309 * @param line the line in the source
310 * @param column the column in the source
311 */
312 public record Location(String sourceRef, int line, int column) {
313
314 /**
315 * The location value, {@code null}, indicating no location information.
316 */
317 public static final Location NO_LOCATION = null;
318
319 /**
320 * Constructions a location with line and column only.
321 *
322 * @param line the line in the source
323 * @param column the column in the source
324 */
325 public Location(int line, int column) {
326 this(null, line, column);
327 }
328 }
329
330 // Set when op is unbuilt-bound or root, otherwise null when unbuilt
331 // @@@ stable value?
332 Result result;
333
334 // null if not specified
335 // @@@ stable value?
336 Location location;
337
338 final List<Value> operands;
339
340 /**
341 * Constructs an operation from a given operation.
342 * <p>
343 * The constructor defers to the {@link Op#Op(List) operands} constructor passing a list of values computed, in
344 * order, by mapping the given operation's operands using the code context. The constructor also assigns the new
345 * operation's location to the given operation's location, if any.
346 *
347 * @param that the given operation.
348 * @param cc the code context.
349 */
350 protected Op(Op that, CodeContext cc) {
351 List<Value> outputOperands = cc.getValues(that.operands);
352 // Values should be guaranteed to connect to unbuilt blocks since
353 // the context only allows such mappings, assert for clarity
354 assert outputOperands.stream().noneMatch(Value::isBuilt);
355 this.operands = List.copyOf(outputOperands);
356 this.location = that.location;
357 }
358
359 /**
360 * Copies this operation and transforms its bodies, if any.
361 * <p>
362 * Bodies are {@link Body#transform(CodeContext, CodeTransformer) transformed} with the given code context and
363 * code transformer.
364 * @apiNote
365 * To copy an operation use the {@link CodeTransformer#COPYING_TRANSFORMER copying transformer}.
366 *
367 * @param cc the code context.
368 * @param ot the code transformer.
369 * @return the transformed operation.
370 * @see CodeTransformer#COPYING_TRANSFORMER
371 */
372 public abstract Op transform(CodeContext cc, CodeTransformer ot);
373
374 /**
375 * Constructs an operation with a list of operands.
376 *
377 * @param operands the list of operands, a copy of the list is performed if required.
378 * @throws IllegalArgumentException if an operand is built because its declaring block is built.
379 */
380 protected Op(List<? extends Value> operands) {
381 for (Value operand : operands) {
382 if (operand.isBuilt()) {
383 throw new IllegalArgumentException("Operand's declaring block is built: " + operand);
384 }
385 }
386 this.operands = List.copyOf(operands);
387 }
388
389 /**
390 * Sets the originating source location of this operation, if this operation is not built.
391 *
392 * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
393 * @throws IllegalStateException if this operation is built.
394 */
395 public final void setLocation(Location l) {
396 // @@@ Fail if location != null?
397 if (isRoot() || (result != null && result.block.isBuilt())) {
398 throw new IllegalStateException("Built operation");
399 }
400
401 location = l;
402 }
403
404 /**
405 * {@return the originating source location of this operation, otherwise {@code null} if not specified}
406 */
407 public final Location location() {
408 return location;
409 }
410
411 /**
412 * Returns this operation's parent block, otherwise {@code null} if this operation is unbuilt or a root.
413 * <p>
414 * The operation's parent block is the same as the operation result's {@link Value#declaringBlock declaring block}.
415 *
416 * @return operation's parent block, or {@code null} if this operation is unbuilt or a root.
417 * @throws IllegalStateException if this operation is unbuilt-bound.
418 * @see Value#declaringBlock()
419 */
420 @Override
421 public final Block parent() {
422 if (isRoot() || result == null) {
423 return null;
424 }
425
426 if (!result.block.isBuilt()) {
427 throw new IllegalStateException("Unbuilt-bound operation");
428 }
429
430 return result.block;
431 }
432
433 @Override
434 public final List<Body> children() {
435 return bodies();
436 }
437
438 /**
439 * {@return the operation's bodies, as an unmodifiable list}
440 * @implSpec this implementation returns an unmodifiable empty list.
441 * @see #children()
442 */
443 public List<Body> bodies() {
444 return List.of();
445 }
446
447 /**
448 * {@return the operation's result type}
449 */
450 public abstract TypeElement resultType();
451
452
453 /**
454 * Returns the operation's result, otherwise {@code null} if this operation is unbuilt or a
455 * root.
456 *
457 * @return the operation's result, or {@code null} if this operation is unbuilt or a root.
458 */
459 public final Result result() {
460 return result == Result.ROOT_RESULT ? null : result;
461 }
462
463 /**
464 * {@return the operation's operands, as an unmodifiable list}
465 */
466 public final List<Value> operands() {
467 return operands;
468 }
469
470 /**
471 * {@return the operation's successors, as an unmodifiable list}
472 * @implSpec this implementation returns an unmodifiable empty list.
473 */
474 public List<Block.Reference> successors() {
475 return List.of();
476 }
477
478 /**
479 * Returns the operation's function type.
480 * <p>
481 * The function type's result type is the operation's result type and its parameter types are the
482 * operation's operand types, in order.
483 *
484 * @return the function type
485 */
486 public final FunctionType opType() {
487 List<TypeElement> operandTypes = operands.stream().map(Value::type).toList();
488 return CoreType.functionType(resultType(), operandTypes);
489 }
490
491 /**
492 * Computes values captured by this operation. A captured value is a value that is used
493 * but not declared by any descendant operation of this operation.
494 * <p>
495 * The order of the captured values is first use encountered in depth
496 * first search of this operation's descendant operations.
497 *
498 * @return the list of captured values, modifiable
499 * @see Body#capturedValues()
500 */
501 public final List<Value> capturedValues() {
502 Set<Value> cvs = new LinkedHashSet<>();
503
504 Deque<Body> bodyStack = new ArrayDeque<>();
505 for (Body childBody : bodies()) {
506 Body.capturedValues(cvs, bodyStack, childBody);
507 }
508 return new ArrayList<>(cvs);
509 }
510
511 /**
512 * Builds this operation to become a built root operation. After this operation is built its
513 * {@link #result result} and {@link #parent parent} will always be {@code null}.
514 * <p>
515 * This method is idempotent.
516 *
517 * @throws IllegalStateException if this operation is unbuilt-bound.
518 * @see #isRoot()
519 */
520 public final void buildAsRoot() {
521 if (result == Result.ROOT_RESULT) {
522 return;
523 }
524 if (result != null) {
525 throw new IllegalStateException("Operation is unbuilt-bound to a parent block");
526 }
527 result = Result.ROOT_RESULT;
528 }
529
530 /**
531 * {@return {@code true} if this operation is a root operation.}
532 * @see #buildAsRoot()
533 * @see #isBound()
534 * */
535 public final boolean isRoot() {
536 return result == Result.ROOT_RESULT;
537 }
538
539 /**
540 * {@return {@code true} if this operation is a bound to a result and parent block.}
541 * @see #buildAsRoot()
542 * @see #isRoot()
543 * */
544 public final boolean isBound() {
545 return !isRoot() && result != null;
546 }
547
548 /**
549 * Externalizes this operation's name as a string.
550 * @implSpec this implementation returns the result of the expression {@code this.getClass().getName()}.
551 *
552 * @return the operation name
553 */
554 public String externalizeOpName() {
555 return this.getClass().getName();
556 }
557
558 /**
559 * Externalizes this operation's specific state as a map of attributes.
560 *
561 * <p>A null attribute value is represented by the constant
562 * value {@link jdk.incubator.code.extern.ExternalizedOp#NULL_ATTRIBUTE_VALUE}.
563 * @implSpec this implementation returns an unmodifiable empty map.
564 *
565 * @return the operation's externalized state, as an unmodifiable map
566 */
567 public Map<String, Object> externalize() {
568 return Map.of();
569 }
570
571 /**
572 * Returns the textual form of this operation.
573 *
574 * @return the textual form of this operation.
575 */
576 public final String toText() {
577 return OpWriter.toText(this);
578 }
579
580
581 /**
582 * Returns the code model of a reflectable lambda expression or method reference.
583 *
584 * @param fiInstance a functional interface instance that is the result of a reflectable lambda expression or
585 * method reference.
586 * @return the code model, or an empty optional if the functional interface instance is not the result of a
587 * reflectable lambda expression or method reference.
588 * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
589 * model is not the same as the Java version used at runtime to load the code model.
590 * @apiNote if the functional interface instance is a proxy instance, then the code model is unavailable and this
591 * method returns an empty optional.
592 */
593 public static Optional<Quoted<JavaOp.LambdaOp>> ofLambda(Object fiInstance) {
594 Object oq = fiInstance;
595 if (Proxy.isProxyClass(oq.getClass())) {
596 // @@@ The interpreter implements interpretation of
597 // lambdas using a proxy whose invocation handler
598 // supports the internal protocol to access the quoted instance
599 oq = Proxy.getInvocationHandler(oq);
600 }
601
602 Method method;
603 try {
604 method = oq.getClass().getDeclaredMethod("__internal_quoted");
605 } catch (NoSuchMethodException e) {
606 return Optional.empty();
607 }
608 method.setAccessible(true);
609
610 Quoted<?> q;
611 try {
612 q = (Quoted<?>) method.invoke(oq);
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 if (!(q.op() instanceof JavaOp.LambdaOp)) {
621 // This can only happen if the stored model is invalid
622 throw new RuntimeException("Invalid code model for lambda expression : " + q);
623 }
624 @SuppressWarnings("unchecked")
625 Quoted<JavaOp.LambdaOp> lq = (Quoted<JavaOp.LambdaOp>) q;
626 return Optional.of(lq);
627 }
628
629 /**
630 * Returns the code model of a reflectable method.
631 *
632 * @param method the method.
633 * @return the code model, or an empty optional if the method is not reflectable.
634 * @throws UnsupportedOperationException if the Java version used at compile time to generate and store the code
635 * model is not the same as the Java version used at runtime to load the code model.
636 */
637 // @@@ Make caller sensitive with the same access control as invoke
638 // and throwing IllegalAccessException
639 // @CallerSensitive
640 @SuppressWarnings("unchecked")
641 public static Optional<FuncOp> ofMethod(Method method) {
642 return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess()
643 .setCodeModelIfNeeded(method, Op::createCodeModel);
644 }
645
646 private static Optional<FuncOp> createCodeModel(Method method) {
647 char[] sig = MethodRef.method(method).toString().toCharArray();
648 for (int i = 0; i < sig.length; i++) {
649 switch (sig[i]) {
650 case '.', ';', '[', '/': sig[i] = '$';
651 }
652 }
653 String opMethodName = new String(sig);
654 Method opMethod;
655 try {
656 // @@@ Use method handle with full power mode
657 opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName);
658 } catch (NoSuchMethodException e) {
659 return Optional.empty();
660 }
661 opMethod.setAccessible(true);
662 try {
663 FuncOp funcOp = (FuncOp) opMethod.invoke(null);
664 return Optional.of(funcOp);
665 } catch (ReflectiveOperationException e) {
666 // op method may throw UOE in case java compile time version doesn't match runtime version
667 if (e.getCause() instanceof UnsupportedOperationException uoe) {
668 throw uoe;
669 }
670 throw new RuntimeException(e);
671 }
672 }
673
674 /**
675 * Returns the code model of provided executable element (if any).
676 * <p>
677 * If the executable element has a code model then it will be an instance of
678 * {@code java.lang.reflect.code.op.CoreOps.FuncOp}.
679 * Note: due to circular dependencies we cannot refer to the type explicitly.
680 *
681 * @param processingEnvironment the annotation processing environment
682 * @param e the executable element.
683 * @return the code model of the provided executable element (if any).
684 */
685 public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
686 if (e.getModifiers().contains(Modifier.ABSTRACT) ||
687 e.getModifiers().contains(Modifier.NATIVE)) {
688 return Optional.empty();
689 }
690
691 Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
692 ReflectMethods reflectMethods = ReflectMethods.instance(context);
693 Attr attr = Attr.instance(context);
694 JavacElements elements = JavacElements.instance(context);
695 JavacTrees javacTrees = JavacTrees.instance(context);
696 TreeMaker make = TreeMaker.instance(context);
697 try {
698 JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
699 JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
700 ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
701 FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
702 attribBlock -> {
703 try {
704 return reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make);
705 } catch (Throwable ex) {
706 // this might happen if the source code contains errors
707 return null;
708 }
709 });
710 return Optional.ofNullable(op);
711 } catch (RuntimeException ex) { // ReflectMethods.UnsupportedASTException
712 // some other error occurred when attempting to attribute the method
713 // @@@ better report of error
714 ex.printStackTrace();
715 return Optional.empty();
716 }
717 }
718 }