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