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         final Op op;
228 
229         Result(Block block, Op op) {
230             super(block, op.resultType());
231 
232             this.op = op;
233         }
234 
235         @Override
236         public String toString() {
237             return "%result@" + Integer.toHexString(hashCode());
238         }
239 
240         @Override
241         public Set<Value> dependsOn() {
242             Set<Value> depends = new LinkedHashSet<>(op.operands());
243             if (op instanceof Terminating) {
244                 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
245             }
246 
247             return Collections.unmodifiableSet(depends);
248         }
249 
250         /**
251          * Returns the result's operation.
252          *
253          * @return the result's operation.
254          */
255         public Op op() {
256             return op;
257         }
258     }
259 
260     // Set when op is bound to block, otherwise null when unbound
261     Result result;
262 
263     // null if not specified
264     Location location;
265 
266     final String name;
267 
268     final List<Value> operands;
269 
270     /**
271      * Constructs an operation by copying given operation.
272      *
273      * @param that the operation to copy.
274      * @param cc   the copy context.
275      * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list
276      * values computed, in order, by mapping the operation's operands using the copy context.
277      */
278     protected Op(Op that, CopyContext cc) {
279         this(that.name, cc.getValues(that.operands));
280         this.location = that.location;
281     }
282 
283     /**
284      * Copies this operation and its bodies, if any.
285      * <p>
286      * The returned operation is structurally identical to this operation and is otherwise independent
287      * of the values declared and used.
288      *
289      * @return the copied operation.
290      */
291     public Op copy() {
292         return transform(CopyContext.create(), OpTransformer.COPYING_TRANSFORMER);
293     }
294 
295     /**
296      * Copies this operation and its bodies, if any.
297      * <p>
298      * The returned operation is structurally identical to this operation and is otherwise independent
299      * of the values declared and used.
300      *
301      * @param cc the copy context.
302      * @return the copied operation.
303      */
304     public Op copy(CopyContext cc) {
305         return transform(cc, OpTransformer.COPYING_TRANSFORMER);
306     }
307 
308     /**
309      * Copies this operation and transforms its bodies, if any.
310      * <p>
311      * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and
312      * operation transformer.
313      *
314      * @param cc the copy context.
315      * @param ot the operation transformer.
316      * @return the transformed operation.
317      */
318     public abstract Op transform(CopyContext cc, OpTransformer ot);
319 
320     /**
321      * Constructs an operation with a name and list of operands.
322      *
323      * @param name       the operation name.
324      * @param operands   the list of operands, a copy of the list is performed if required.
325      */
326     protected Op(String name, List<? extends Value> operands) {
327         this.name = name;
328         this.operands = List.copyOf(operands);
329     }
330 
331     /**
332      * Sets the originating source location of this operation, if unbound.
333      *
334      * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
335      * @throws IllegalStateException if this operation is bound
336      */
337     public final void setLocation(Location l) {
338         // @@@ Fail if location != null?
339         if (result != null && result.block.isBound()) {
340             throw new IllegalStateException();
341         }
342 
343         location = l;
344     }
345 
346     /**
347      * {@return the originating source location of this operation, otherwise {@code null} if not specified}
348      */
349     public final Location location() {
350         return location;
351     }
352 
353     /**
354      * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block.
355      *
356      * @return operation's parent block, or {@code null} if the operation is not assigned to a block.
357      */
358     @Override
359     public final Block parent() {
360         if (result == null) {
361             return null;
362         }
363 
364         if (!result.block.isBound()) {
365             throw new IllegalStateException("Parent block is partially constructed");
366         }
367 
368         return result.block;
369     }
370 
371     @Override
372     public final List<Body> children() {
373         return bodies();
374     }
375 
376     /**
377      * {@return the operation's bodies, as an unmodifiable list}
378      * @implSpec this implementation returns an unmodifiable empty list.
379      */
380     public List<Body> bodies() {
381         return List.of();
382     }
383 
384     /**
385      * Returns the operation's result, otherwise {@code null} if the operation is not assigned to a block.
386      *
387      * @return the operation's result, or {@code null} if not assigned to a block.
388      */
389     public final Result result() {
390         return result;
391     }
392 
393     /**
394      * {@return the operation name}
395      */
396     public String opName() {
397         return name;
398     }
399 
400     /**
401      * {@return the operation's operands, as an unmodifiable list}
402      */
403     public List<Value> operands() {
404         return operands;
405     }
406 
407     /**
408      * {@return the operation's successors, as an unmodifiable list}
409      */
410     public List<Block.Reference> successors() {
411         return List.of();
412     }
413 
414     /**
415      * {@return the operation's result type}
416      */
417     public abstract TypeElement resultType();
418 
419     /**
420      * Returns the operation's function type.
421      * <p>
422      * The function type's result type is the operation's result type and the function type's parameter types are the
423      * operation's operand types, in order.
424      *
425      * @return the function type
426      */
427     public FunctionType opType() {
428         List<TypeElement> operandTypes = operands.stream().map(Value::type).toList();
429         return CoreType.functionType(resultType(), operandTypes);
430     }
431 
432     /**
433      * Externalizes the operation's state as a map of attributes.
434      *
435      * <p>A null attribute value is represented by the constant
436      * value {@link jdk.incubator.code.extern.ExternalizedOp#NULL_ATTRIBUTE_VALUE}.
437      *
438      * @return the operation's externalized state, as an unmodifiable map
439      */
440     public Map<String, Object> externalize() {
441         return Map.of();
442     }
443 
444     /**
445      * Computes values captured by this operation. A captured value is a value that dominates
446      * this operation and is used by a descendant operation.
447      * <p>
448      * The order of the captured values is first use encountered in depth
449      * first search of this operation's descendant operations.
450      *
451      * @return the list of captured values, modifiable
452      * @see Body#capturedValues()
453      */
454     public List<Value> capturedValues() {
455         Set<Value> cvs = new LinkedHashSet<>();
456 
457         capturedValues(cvs, new ArrayDeque<>(), this);
458         return new ArrayList<>(cvs);
459     }
460 
461     static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Op op) {
462         for (Body childBody : op.bodies()) {
463             Body.capturedValues(capturedValues, bodyStack, childBody);
464         }
465     }
466 
467     /**
468      * Returns the textual form of this operation.
469      *
470      * @return the textual form of this operation.
471      */
472     public String toText() {
473         return OpWriter.toText(this);
474     }
475 
476 
477     /**
478      * Returns the quoted code model of the given quotable reference, if present.
479      *
480      * @param q the quotable reference.
481      * @return the quoted code model or an empty optional if the
482      *         quoted code model is unavailable.
483      * @apiNote If the quotable reference is a proxy instance, then the
484      *          quoted code model is unavailable and this method
485      *          returns an empty optional.
486      * @since 99
487      */
488     public static Optional<Quoted> ofQuotable(Quotable q) {
489         Object oq = q;
490         if (Proxy.isProxyClass(oq.getClass())) {
491             // @@@ The interpreter implements interpretation of
492             // lambdas using a proxy whose invocation handler
493             // supports the internal protocol to access the quoted instance
494             oq = Proxy.getInvocationHandler(oq);
495         }
496 
497         Method method;
498         try {
499             method = oq.getClass().getDeclaredMethod("__internal_quoted");
500         } catch (NoSuchMethodException e) {
501             return Optional.empty();
502         }
503         method.setAccessible(true);
504 
505         Quoted quoted;
506         try {
507             quoted = (Quoted) method.invoke(oq);
508         } catch (InvocationTargetException | IllegalAccessException e) {
509             throw new RuntimeException(e);
510         }
511         return Optional.of(quoted);
512     }
513 
514     /**
515      * Returns the code model of the given method's body, if present.
516      *
517      * @param method the method.
518      * @return the code model of the method body.
519      * @since 99
520      */
521     // @@@ Make caller sensitive with the same access control as invoke
522     // and throwing IllegalAccessException
523     // @CallerSensitive
524     @SuppressWarnings("unchecked")
525     public static Optional<FuncOp> ofMethod(Method method) {
526         return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess()
527                 .setCodeModelIfNeeded(method, Op::createCodeModel);
528     }
529 
530     private static Optional<FuncOp> createCodeModel(Method method) {
531         char[] sig = MethodRef.method(method).toString().toCharArray();
532         for (int i = 0; i < sig.length; i++) {
533             switch (sig[i]) {
534                 case '.', ';', '[', '/': sig[i] = '$';
535             }
536         }
537         String opMethodName = new String(sig);
538         Method opMethod;
539         try {
540             // @@@ Use method handle with full power mode
541             opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName);
542         } catch (NoSuchMethodException e) {
543             return Optional.empty();
544         }
545         opMethod.setAccessible(true);
546         try {
547             FuncOp funcOp = (FuncOp) opMethod.invoke(null);
548             return Optional.of(funcOp);
549         } catch (ReflectiveOperationException e) {
550             throw new RuntimeException(e);
551         }
552     }
553 
554     /**
555      * Returns the code model of provided executable element (if any).
556      * <p>
557      * If the executable element has a code model then it will be an instance of
558      * {@code java.lang.reflect.code.op.CoreOps.FuncOp}.
559      * Note: due to circular dependencies we cannot refer to the type explicitly.
560      *
561      * @implSpec The default implementation unconditionally returns an empty optional.
562      * @param e the executable element.
563      * @return the code model of the provided executable element (if any).
564      * @since 99
565      */
566     public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
567         if (e.getModifiers().contains(Modifier.ABSTRACT) ||
568                 e.getModifiers().contains(Modifier.NATIVE)) {
569             return Optional.empty();
570         }
571 
572         Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
573         ReflectMethods reflectMethods = ReflectMethods.instance(context);
574         Attr attr = Attr.instance(context);
575         JavacElements elements = JavacElements.instance(context);
576         JavacTrees javacTrees = JavacTrees.instance(context);
577         TreeMaker make = TreeMaker.instance(context);
578         try {
579             JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
580             JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
581             ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
582             FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
583                     attribBlock -> reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make));
584             return Optional.of(op);
585         } catch (RuntimeException ex) {  // ReflectMethods.UnsupportedASTException
586             // some other error occurred when attempting to attribute the method
587             // @@@ better report of error
588             ex.printStackTrace();
589             return Optional.empty();
590         }
591     }
592 }