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 }