1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package 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.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.InvocationTargetException;
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 functionality.
56 * <p>
57 * An operation might model the addition of two 32-bit integers, or a Java method call.
58 * Alternatively an operation may model something more complex like method bodies, lambda bodies, or
59 * try/catch/finally statements. In this case such an operation will contain one or more bodies modelling
60 * the nested structure.
61 */
62 public non-sealed abstract class Op implements CodeElement<Op, Body> {
63
64 /**
65 * An operation characteristic indicating the operation is pure and has no side effects.
66 */
67 public interface Pure {
68 }
69
70 /**
71 * An operation characteristic indicating the operation has one or more bodies.
72 */
73 public interface Nested {
74 List<Body> bodies();
75 }
76
77 /**
78 * An operation characteristic indicating the operation represents a loop
79 */
80 public interface Loop extends Nested {
81 Body loopBody();
82 }
83
84 /**
85 * An operation characteristic indicating the operation has one or more bodies,
86 * all of which are isolated.
87 */
88 public interface Isolated extends Nested {
89 }
90
91 /**
92 * An operation characteristic indicating the operation is invokable, so the operation may be interpreted
93 * or compiled.
94 */
95 public interface Invokable extends Nested {
96 /**
97 * {@return the body of the invokable operation.}
98 */
99 Body body();
100
101 /**
102 * {@return the function type describing the invokable operation's parameter types and return type.}
103 */
104 FunctionType invokableType();
105
106 /**
107 * {@return the entry block parameters of this operation's body}
108 */
109 default List<Block.Parameter> parameters() {
110 return body().entryBlock().parameters();
111 }
112
113 /**
114 * Computes values captured by this invokable operation's body.
115 *
116 * @return the captured values.
117 * @see Body#capturedValues()
118 */
119 default List<Value> capturedValues() {
120 return List.of();
121 }
122 }
123
124 /**
125 * An operation characteristic indicating the operation can replace itself with a lowered form.
126 */
127 // @@@ Hide this abstraction within JavaOp?
128 public interface Lowerable {
129
130 /**
131 * Lowers this operation into the block builder, commonly replacing nested structure
132 * with interconnected basic blocks. The previous lowering code transformation
133 * is used to compose with a lowering transformation that is applied to bodies
134 * of this operation, ensuring lowering is applied consistently to nested content.
135 *
136 * @param b the block builder
137 * @param opT the previous lowering code transformation, may be {@code null}
138 * @return the block builder to use for further building
139 */
140 Block.Builder lower(Block.Builder b, OpTransformer opT);
141
142 /**
143 * Returns a composed code transformer that composes with an operation transformer function adapted to lower
144 * operations.
145 * <p>
146 * This method behaves as if it returns the result of the following expression:
147 * {@snippet lang = java:
148 * OpTransformer.andThen(before, lowering(before, f));
149 *}
150 *
151 * @param before the code transformer to apply before
152 * @param f the operation transformer function to apply after
153 * @return the composed code transformer
154 */
155 static OpTransformer andThenLowering(OpTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) {
156 return OpTransformer.andThen(before, lowering(before, f));
157 }
158
159 /**
160 * Returns an adapted operation transformer function that adapts an operation transformer function
161 * {@code f} to also transform lowerable operations.
162 * <p>
163 * The adapted operation transformer function first applies a block builder and operation
164 * to the operation transformer function {@code f}.
165 * If the result is not {@code null} then the result is returned.
166 * Otherwise, if the operation is a lowerable operation then the result of applying the
167 * block builder and code transformer {@code before} to {@link Lowerable#lower lower}
168 * of the lowerable operation is returned.
169 * Otherwise, the operation is copied by applying it to {@link Block.Builder#op op} of the block builder,
170 * and the block builder is returned.
171 *
172 * @param before the code transformer to apply for lowering
173 * @param f the operation transformer function to apply after
174 * @return the adapted operation transformer function
175 */
176 static BiFunction<Block.Builder, Op, Block.Builder> lowering(OpTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) {
177 return (block, op) -> {
178 Block.Builder b = f.apply(block, op);
179 if (b == null) {
180 if (op instanceof Lowerable lop) {
181 block = lop.lower(block, before);
182 } else {
183 block.op(op);
184 }
185 } else {
186 block = b;
187 }
188 return block;
189 };
190 }
191 }
192
193 /**
194 * An operation characteristic indicating the operation is a terminating operation
195 * occurring as the last operation in a block.
196 * <p>
197 * A terminating operation passes control to either another block within the same parent body
198 * or to that parent body.
199 */
200 public interface Terminating {
201 }
202
203 /**
204 * An operation characteristic indicating the operation is a body terminating operation
205 * occurring as the last operation in a block.
206 * <p>
207 * A body terminating operation passes control back to its nearest ancestor body.
208 */
209 public interface BodyTerminating extends Terminating {
210 }
211
212 /**
213 * An operation characteristic indicating the operation is a block terminating operation
214 * occurring as the last operation in a block.
215 * <p>
216 * The operation has one or more successors to other blocks within the same parent body, and passes
217 * control to one of those blocks.
218 */
219 public interface BlockTerminating extends Terminating {
220 List<Block.Reference> successors();
221 }
222
223 /**
224 * A value that is the result of an operation.
225 */
226 public static final class Result extends Value {
227
228 /**
229 * If assigned to an operation result, it indicates the operation is sealed
230 */
231 private static final Result SEALED_RESULT = new Result();
232
233 final Op op;
234
235 private Result() {
236 super(null, null);
237 this.op = null;
238 }
239
240 Result(Block block, Op op) {
241 super(block, op.resultType());
242
243 this.op = op;
244 }
245
246 @Override
247 public String toString() {
248 return "%result@" + Integer.toHexString(hashCode());
249 }
250
251 @Override
252 public Set<Value> dependsOn() {
253 Set<Value> depends = new LinkedHashSet<>(op.operands());
254 if (op instanceof Terminating) {
255 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
256 }
257
258 return Collections.unmodifiableSet(depends);
259 }
260
261 /**
262 * Returns the result's operation.
263 *
264 * @return the result's operation.
265 */
266 public Op op() {
267 return op;
268 }
269 }
270
271 // Set when op is bound to block, otherwise null when unbound
272 // @@@ stable value?
273 Result result;
274
275 // null if not specified
276 // @@@ stable value?
277 Location location;
278
279 final List<Value> operands;
280
281 /**
282 * Constructs an operation from a given operation.
283 * <p>
284 * The constructor defers to the {@link Op#Op(List) operands} constructor passing a list of values computed, in
285 * order, by mapping the given operation's operands using the copy context. The constructor also assigns the new
286 * operation's location to the given operation's location, if any.
287 *
288 * @param that the given operation.
289 * @param cc the copy context.
290 */
291 protected Op(Op that, CopyContext cc) {
292 this(cc.getValues(that.operands));
293 this.location = that.location;
294 }
295
296 /**
297 * Copies this operation and transforms its bodies, if any.
298 * <p>
299 * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and
300 * operation transformer.
301 * @apiNote
302 * To copy an operation use the {@link OpTransformer#COPYING_TRANSFORMER copying transformer}.
303 *
304 * @param cc the copy context.
305 * @param ot the operation transformer.
306 * @return the transformed operation.
307 */
308 public abstract Op transform(CopyContext cc, OpTransformer ot);
309
310 /**
311 * Constructs an operation with a list of operands.
312 *
313 * @param operands the list of operands, a copy of the list is performed if required.
314 */
315 protected Op(List<? extends Value> operands) {
316 this.operands = List.copyOf(operands);
317 }
318
319 /**
320 * Sets the originating source location of this operation, if unbound.
321 *
322 * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
323 * @throws IllegalStateException if this operation is bound or sealed
324 */
325 public final void setLocation(Location l) {
326 // @@@ Fail if location != null?
327 if (isSealed() || (result != null && result.block.isBound())) {
328 throw new IllegalStateException();
329 }
330
331 location = l;
332 }
333
334 /**
335 * {@return the originating source location of this operation, otherwise {@code null} if not specified}
336 */
337 public final Location location() {
338 return location;
339 }
340
341 /**
342 * Returns this operation's parent block, otherwise {@code null} if the operation is unbound or sealed.
343 *
344 * @return operation's parent block, or {@code null} if the operation is unbound or sealed.
345 */
346 @Override
347 public final Block parent() {
348 if (isSealed() || result == null) {
349 return null;
350 }
351
352 if (!result.block.isBound()) {
353 throw new IllegalStateException("Parent block is partially constructed");
354 }
355
356 return result.block;
357 }
358
359 @Override
360 public final List<Body> children() {
361 return bodies();
362 }
363
364 /**
365 * {@return the operation's bodies, as an unmodifiable list}
366 * @implSpec this implementation returns an unmodifiable empty list.
367 */
368 public List<Body> bodies() {
369 return List.of();
370 }
371
372 /**
373 * {@return the operation's result type}
374 */
375 public abstract TypeElement resultType();
376
377 /**
378 * Returns the operation's result, otherwise {@code null} if the operation is unbound or sealed.
379 *
380 * @return the operation's result, or {@code null} if unbound or sealed.
381 */
382 public final Result result() {
383 return result == Result.SEALED_RESULT ? null : result;
384 }
385
386 /**
387 * {@return the operation's operands, as an unmodifiable list}
388 */
389 public final List<Value> operands() {
390 return operands;
391 }
392
393 /**
394 * {@return the operation's successors, as an unmodifiable list}
395 * @implSpec this implementation returns an unmodifiable empty list.
396 */
397 public List<Block.Reference> successors() {
398 return List.of();
399 }
400
401 /**
402 * Returns the operation's function type.
403 * <p>
404 * The function type's result type is the operation's result type and the function type's parameter types are the
405 * operation's operand types, in order.
406 *
407 * @return the function type
408 */
409 public final FunctionType opType() {
410 List<TypeElement> operandTypes = operands.stream().map(Value::type).toList();
411 return CoreType.functionType(resultType(), operandTypes);
412 }
413
414 /**
415 * Computes values captured by this operation. A captured value is a value that dominates
416 * this operation and is used by a descendant operation.
417 * <p>
418 * The order of the captured values is first use encountered in depth
419 * first search of this operation's descendant operations.
420 *
421 * @return the list of captured values, modifiable
422 * @see Body#capturedValues()
423 */
424 public final List<Value> capturedValues() {
425 Set<Value> cvs = new LinkedHashSet<>();
426
427 Deque<Body> bodyStack = new ArrayDeque<>();
428 for (Body childBody : bodies()) {
429 Body.capturedValues(cvs, bodyStack, childBody);
430 }
431 return new ArrayList<>(cvs);
432 }
433
434 /**
435 * Seals this operation. After this operation is sealed its {@link #result result} and {@link #parent parent} are guaranteed to always be {@code null}.
436 * <p>
437 * If a sealed operation is {@link Block.Builder#op appended} to a {@link Block.Builder} then it is
438 * treated as if the operation is bound, and therefore the sealed operation will be transformed.
439 * <p>
440 * Sealing is idempotent if the operation is already sealed.
441 *
442 * @throws IllegalStateException if this operation is bound.
443 */
444 public final void seal() {
445 if (result == Result.SEALED_RESULT) {
446 return;
447 }
448 if (result != null) {
449 throw new IllegalStateException("Operation cannot be sealed since it bound to a parent block");
450 }
451 result = Result.SEALED_RESULT;
452 }
453
454 /**
455 * Returns {@code true} if this operation is sealed.
456 * @return {@code true} if this operation is sealed.
457 * @see #seal()
458 * */
459 public final boolean isSealed() {
460 return result == Result.SEALED_RESULT;
461 }
462
463 /**
464 * Externalizes this operation's name as a string.
465 * @implSpec this implementation returns the result of the expression {@code this.getClass().getName()}.
466 * @return the operation name
467 */
468 public String externalizeOpName() {
469 return this.getClass().getName();
470 }
471
472 /**
473 * Externalizes this operation's specific state as a map of attributes.
474 *
475 * <p>A null attribute value is represented by the constant
476 * value {@link jdk.incubator.code.extern.ExternalizedOp#NULL_ATTRIBUTE_VALUE}.
477 * @implSpec this implementation returns an unmodifiable empty map.
478 *
479 * @return the operation's externalized state, as an unmodifiable map
480 */
481 public Map<String, Object> externalize() {
482 return Map.of();
483 }
484
485 /**
486 * Returns the textual form of this operation.
487 *
488 * @return the textual form of this operation.
489 */
490 public final String toText() {
491 return OpWriter.toText(this);
492 }
493
494
495 /**
496 * Returns the quoted code model of the given quotable reference, if present.
497 *
498 * @param q the quotable reference.
499 * @return the quoted code model or an empty optional if the
500 * quoted code model is unavailable.
501 * @apiNote If the quotable reference is a proxy instance, then the
502 * quoted code model is unavailable and this method
503 * returns an empty optional.
504 * @since 99
505 */
506 public static Optional<Quoted> ofQuotable(Quotable q) {
507 Object oq = q;
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 quoted;
524 try {
525 quoted = (Quoted) method.invoke(oq);
526 } catch (InvocationTargetException | IllegalAccessException e) {
527 throw new RuntimeException(e);
528 }
529 return Optional.of(quoted);
530 }
531
532 /**
533 * Returns the code model of the given method's body, if present.
534 *
535 * @param method the method.
536 * @return the code model of the method body.
537 * @since 99
538 */
539 // @@@ Make caller sensitive with the same access control as invoke
540 // and throwing IllegalAccessException
541 // @CallerSensitive
542 @SuppressWarnings("unchecked")
543 public static Optional<FuncOp> ofMethod(Method method) {
544 return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess()
545 .setCodeModelIfNeeded(method, Op::createCodeModel);
546 }
547
548 private static Optional<FuncOp> createCodeModel(Method method) {
549 char[] sig = MethodRef.method(method).toString().toCharArray();
550 for (int i = 0; i < sig.length; i++) {
551 switch (sig[i]) {
552 case '.', ';', '[', '/': sig[i] = '$';
553 }
554 }
555 String opMethodName = new String(sig);
556 Method opMethod;
557 try {
558 // @@@ Use method handle with full power mode
559 opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName);
560 } catch (NoSuchMethodException e) {
561 return Optional.empty();
562 }
563 opMethod.setAccessible(true);
564 try {
565 FuncOp funcOp = (FuncOp) opMethod.invoke(null);
566 return Optional.of(funcOp);
567 } catch (ReflectiveOperationException e) {
568 throw new RuntimeException(e);
569 }
570 }
571
572 /**
573 * Returns the code model of provided executable element (if any).
574 * <p>
575 * If the executable element has a code model then it will be an instance of
576 * {@code java.lang.reflect.code.op.CoreOps.FuncOp}.
577 * Note: due to circular dependencies we cannot refer to the type explicitly.
578 *
579 * @implSpec The default implementation unconditionally returns an empty optional.
580 * @param e the executable element.
581 * @return the code model of the provided executable element (if any).
582 * @since 99
583 */
584 public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
585 if (e.getModifiers().contains(Modifier.ABSTRACT) ||
586 e.getModifiers().contains(Modifier.NATIVE)) {
587 return Optional.empty();
588 }
589
590 Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
591 ReflectMethods reflectMethods = ReflectMethods.instance(context);
592 Attr attr = Attr.instance(context);
593 JavacElements elements = JavacElements.instance(context);
594 JavacTrees javacTrees = JavacTrees.instance(context);
595 TreeMaker make = TreeMaker.instance(context);
596 try {
597 JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
598 JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
599 ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
600 FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
601 attribBlock -> {
602 try {
603 return reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make);
604 } catch (Throwable ex) {
605 // this might happen if the source code contains errors
606 return null;
607 }
608 });
609 return Optional.ofNullable(op);
610 } catch (RuntimeException ex) { // ReflectMethods.UnsupportedASTException
611 // some other error occurred when attempting to attribute the method
612 // @@@ better report of error
613 ex.printStackTrace();
614 return Optional.empty();
615 }
616 }
617 }