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 java.io.OutputStream;
 29 import java.io.OutputStreamWriter;
 30 import java.io.Writer;
 31 
 32 import com.sun.tools.javac.api.JavacScope;
 33 import com.sun.tools.javac.api.JavacTrees;
 34 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 35 import com.sun.tools.javac.comp.Attr;
 36 import com.sun.tools.javac.model.JavacElements;
 37 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
 38 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
 39 import com.sun.tools.javac.tree.TreeMaker;
 40 import com.sun.tools.javac.util.Context;
 41 import jdk.incubator.code.internal.ReflectMethods;
 42 import jdk.incubator.code.op.CoreOp.FuncOp;
 43 import jdk.incubator.code.type.FunctionType;
 44 import jdk.incubator.code.type.MethodRef;
 45 import jdk.incubator.code.writer.OpWriter;
 46 import jdk.internal.access.SharedSecrets;
 47 
 48 import javax.annotation.processing.ProcessingEnvironment;
 49 import javax.lang.model.element.ExecutableElement;
 50 import javax.lang.model.element.Modifier;
 51 import java.lang.reflect.InvocationTargetException;
 52 import java.lang.reflect.Method;
 53 import java.lang.reflect.Proxy;
 54 import java.nio.charset.StandardCharsets;
 55 import java.util.*;
 56 import java.util.function.BiFunction;
 57 
 58 /**
 59  * An operation modelling a unit of functionality.
 60  * <p>
 61  * An operation might model the addition of two 32-bit integers, or a Java method call.
 62  * Alternatively an operation may model something more complex like method bodies, lambda bodies, or
 63  * try/catch/finally statements. In this case such an operation will contain one or more bodies modelling
 64  * the nested structure.
 65  */
 66 public non-sealed abstract class Op implements CodeElement<Op, Body> {
 67 
 68     /**
 69      * An operation characteristic indicating the operation is pure and has no side effects.
 70      */
 71     public interface Pure {
 72     }
 73 
 74     /**
 75      * An operation characteristic indicating the operation has one or more bodies.
 76      */
 77     public interface Nested {
 78         List<Body> bodies();
 79     }
 80 
 81     /**
 82      * An operation characteristic indicating the operation represents a loop
 83      */
 84     public interface Loop extends Nested {
 85         Body loopBody();
 86     }
 87 
 88     /**
 89      * An operation characteristic indicating the operation has one or more bodies,
 90      * all of which are isolated.
 91      */
 92     public interface Isolated extends Nested {
 93     }
 94 
 95     /**
 96      * An operation characteristic indicating the operation is invokable, so the operation may be interpreted
 97      * or compiled.
 98      */
 99     public interface Invokable extends Nested {
100         /**
101          * {@return the body of the invokable operation.}
102          */
103         Body body();
104 
105         /**
106          * {@return the function type describing the invokable operation's parameter types and return type.}
107          */
108         FunctionType invokableType();
109 
110         /**
111          * {@return the entry block parameters of this operation's body}
112          */
113         default List<Block.Parameter> parameters() {
114             return body().entryBlock().parameters();
115         }
116 
117         /**
118          * Computes values captured by this invokable operation's body.
119          *
120          * @return the captured values.
121          * @see Body#capturedValues()
122          */
123         default List<Value> capturedValues() {
124             return List.of();
125         }
126     }
127 
128     /**
129      * An operation characteristic indicating the operation can replace itself with a lowered form,
130      * consisting only of operations in the core dialect.
131      */
132     public interface Lowerable {
133         default Block.Builder lower(Block.Builder b) {
134             return lower(b, OpTransformer.NOOP_TRANSFORMER);
135         }
136 
137         Block.Builder lower(Block.Builder b, OpTransformer opT);
138     }
139 
140     /**
141      * An operation characteristic indicating the operation is a terminating operation
142      * occurring as the last operation in a block.
143      * <p>
144      * A terminating operation passes control to either another block within the same parent body
145      * or to that parent body.
146      */
147     public interface Terminating {
148     }
149 
150     /**
151      * An operation characteristic indicating the operation is a body terminating operation
152      * occurring as the last operation in a block.
153      * <p>
154      * A body terminating operation passes control back to its nearest ancestor body.
155      */
156     public interface BodyTerminating extends Terminating {
157     }
158 
159     /**
160      * An operation characteristic indicating the operation is a block terminating operation
161      * occurring as the last operation in a block.
162      * <p>
163      * The operation has one or more successors to other blocks within the same parent body, and passes
164      * control to one of those blocks.
165      */
166     public interface BlockTerminating extends Terminating {
167         List<Block.Reference> successors();
168     }
169 
170     /**
171      * A value that is the result of an operation.
172      */
173     public static final class Result extends Value {
174         final Op op;
175 
176         Result(Block block, Op op) {
177             super(block, op.resultType());
178 
179             this.op = op;
180         }
181 
182         @Override
183         public Set<Value> dependsOn() {
184             Set<Value> depends = new LinkedHashSet<>(op.operands());
185             if (op instanceof Terminating) {
186                 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
187             }
188 
189             return Collections.unmodifiableSet(depends);
190         }
191 
192         /**
193          * Returns the result's operation.
194          *
195          * @return the result's operation.
196          */
197         public Op op() {
198             return op;
199         }
200     }
201 
202     // Set when op is bound to block, otherwise null when unbound
203     Result result;
204 
205     // null if not specified
206     Location location;
207 
208     final String name;
209 
210     final List<Value> operands;
211 
212     /**
213      * Constructs an operation by copying given operation.
214      *
215      * @param that the operation to copy.
216      * @param cc   the copy context.
217      * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list
218      * values computed, in order, by mapping the operation's operands using the copy context.
219      */
220     protected Op(Op that, CopyContext cc) {
221         this(that.name, cc.getValues(that.operands));
222         this.location = that.location;
223     }
224 
225     /**
226      * Copies this operation and its bodies, if any.
227      * <p>
228      * The returned operation is structurally identical to this operation and is otherwise independent
229      * of the values declared and used.
230      *
231      * @return the copied operation.
232      */
233     public Op copy() {
234         return transform(CopyContext.create(), OpTransformer.COPYING_TRANSFORMER);
235     }
236 
237     /**
238      * Copies this operation and its bodies, if any.
239      * <p>
240      * The returned operation is structurally identical to this operation and is otherwise independent
241      * of the values declared and used.
242      *
243      * @param cc the copy context.
244      * @return the copied operation.
245      */
246     public Op copy(CopyContext cc) {
247         return transform(cc, OpTransformer.COPYING_TRANSFORMER);
248     }
249 
250     /**
251      * Copies this operation and transforms its bodies, if any.
252      * <p>
253      * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and
254      * operation transformer.
255      *
256      * @param cc the copy context.
257      * @param ot the operation transformer.
258      * @return the transformed operation.
259      */
260     public abstract Op transform(CopyContext cc, OpTransformer ot);
261 
262     /**
263      * Constructs an operation with a name and list of operands.
264      *
265      * @param name       the operation name.
266      * @param operands   the list of operands, a copy of the list is performed if required.
267      */
268     protected Op(String name, List<? extends Value> operands) {
269         this.name = name;
270         this.operands = List.copyOf(operands);
271     }
272 
273     /**
274      * Sets the originating source location of this operation, if unbound.
275      *
276      * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
277      * @throws IllegalStateException if this operation is bound
278      */
279     public final void setLocation(Location l) {
280         // @@@ Fail if location != null?
281         if (result != null && result.block.isBound()) {
282             throw new IllegalStateException();
283         }
284 
285         location = l;
286     }
287 
288     /**
289      * {@return the originating source location of this operation, otherwise {@code null} if not specified}
290      */
291     public final Location location() {
292         return location;
293     }
294 
295     /**
296      * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block.
297      *
298      * @return operation's parent block, or {@code null} if the operation is not assigned to a block.
299      */
300     @Override
301     public final Block parent() {
302         return parentBlock();
303     }
304 
305     /**
306      * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block.
307      *
308      * @return operation's parent block, or {@code null} if the operation is not assigned to a block.
309      */
310     public final Block parentBlock() {
311         if (result == null) {
312             return null;
313         }
314 
315         if (!result.block.isBound()) {
316             throw new IllegalStateException("Parent block is partially constructed");
317         }
318 
319         return result.block;
320     }
321 
322     @Override
323     public final List<Body> children() {
324         return bodies();
325     }
326 
327     /**
328      * {@return the operation's bodies, as an unmodifiable list}
329      * @implSpec this implementation returns an unmodifiable empty list.
330      */
331     public List<Body> bodies() {
332         return List.of();
333     }
334 
335     /**
336      * Returns the operation's result, otherwise {@code null} if the operation is not assigned to a block.
337      *
338      * @return the operation's result, or {@code null} if not assigned to a block.
339      */
340     public final Result result() {
341         return result;
342     }
343 
344 
345     /**
346      * Returns this operation's nearest ancestor body (the parent body of this operation's parent block),
347      * otherwise {@code null} if the operation is not assigned to a block.
348      *
349      * @return operation's nearest ancestor body, or {@code null} if the operation is not assigned to a block.
350      */
351     public final Body ancestorBody() {
352         if (result == null) {
353             return null;
354         }
355 
356         if (!result.block.isBound()) {
357             throw new IllegalStateException("Parent body is partially constructed");
358         }
359 
360         return result.block.parentBody;
361     }
362 
363     /**
364      * {@return the operation name}
365      */
366     public String opName() {
367         return name;
368     }
369 
370     /**
371      * {@return the operation's operands, as an unmodifiable list}
372      */
373     public List<Value> operands() {
374         return operands;
375     }
376 
377     /**
378      * {@return the operation's successors, as an unmodifiable list}
379      */
380     public List<Block.Reference> successors() {
381         return List.of();
382     }
383 
384     /**
385      * {@return the operation's result type}
386      */
387     public abstract TypeElement resultType();
388 
389     /**
390      * Returns the operation's function type.
391      * <p>
392      * The function type's result type is the operation's result type and the function type's parameter types are the
393      * operation's operand types, in order.
394      *
395      * @return the function type
396      */
397     public FunctionType opType() {
398         List<TypeElement> operandTypes = operands.stream().map(Value::type).toList();
399         return FunctionType.functionType(resultType(), operandTypes);
400     }
401 
402     /**
403      * Traverse the operands of this operation that are the results of prior operations, recursively.
404      * <p>
405      * Traversal is performed in pre-order, reporting the operation of each operand to the visitor.
406      *
407      * @param t   the traversing accumulator
408      * @param v   the visitor
409      * @param <T> accumulator type
410      * @return the traversing accumulator
411      * @apiNote A visitor that implements the abstract method of {@code OpVisitor} and does not override any
412      * other default method will only visit operations. As such a lambda expression or method reference
413      * may be used to visit operations.
414      */
415     public final <T> T traverseOperands(T t, BiFunction<T, Op, T> v) {
416         for (Value arg : operands()) {
417             if (arg instanceof Result or) {
418                 t = v.apply(t, or.op);
419                 t = or.op.traverseOperands(t, v);
420             }
421         }
422 
423         return t;
424     }
425 
426     /**
427      * Computes values captured by this operation. A captured value is a value that dominates
428      * this operation and is used by a descendant operation.
429      * <p>
430      * The order of the captured values is first use encountered in depth
431      * first search of this operation's descendant operations.
432      *
433      * @return the list of captured values, modifiable
434      * @see Body#capturedValues()
435      */
436     public List<Value> capturedValues() {
437         Set<Value> cvs = new LinkedHashSet<>();
438 
439         capturedValues(cvs, new ArrayDeque<>(), this);
440         return new ArrayList<>(cvs);
441     }
442 
443     static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Op op) {
444         for (Body childBody : op.bodies()) {
445             Body.capturedValues(capturedValues, bodyStack, childBody);
446         }
447     }
448 
449     /**
450      * Writes the textual form of this operation to the given output stream, using the UTF-8 character set.
451      *
452      * @param out the stream to write to.
453      */
454     public void writeTo(OutputStream out) {
455         writeTo(new OutputStreamWriter(out, StandardCharsets.UTF_8));
456     }
457 
458     /**
459      * Writes the textual form of this operation to the given writer.
460      *
461      * @param w the writer to write to.
462      */
463     public void writeTo(Writer w) {
464         OpWriter.writeTo(w, this);
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 code model of the Quotable passed in.
479      * @param q the Quotable we want to get its code model.
480      * @return the code model of the Quotable passed in.
481      * @apiNote If the Quotable instance is a proxy instance, then the quoted code model is inaccessible and this method
482      * returns an empty optional.
483      * @since 99
484      */
485     public static Optional<Quoted> ofQuotable(Quotable q) {
486         Object oq = q;
487         if (Proxy.isProxyClass(oq.getClass())) {
488             oq = Proxy.getInvocationHandler(oq);
489         }
490 
491         Method method;
492         try {
493             method = oq.getClass().getMethod("__internal_quoted");
494         } catch (NoSuchMethodException e) {
495             return Optional.empty();
496         }
497         method.setAccessible(true);
498 
499         Quoted quoted;
500         try {
501             quoted = (Quoted) method.invoke(oq);
502         } catch (InvocationTargetException | IllegalAccessException e) {
503             throw new RuntimeException(e);
504         }
505         return Optional.of(quoted);
506     }
507 
508     /**
509      * Returns the code model of the method body, if present.
510      * @return the code model of the method body.
511      * @since 99
512      */
513     // @@@ Make caller sensitive with the same access control as invoke
514     // and throwing IllegalAccessException
515     // @CallerSensitive
516     @SuppressWarnings("unchecked")
517     public static Optional<FuncOp> ofMethod(Method method) {
518         return (Optional<FuncOp>)SharedSecrets.getJavaLangReflectAccess()
519                 .setCodeModelIfNeeded(method, Op::createCodeModel);
520     }
521 
522     private static Optional<FuncOp> createCodeModel(Method method) {
523         char[] sig = MethodRef.method(method).toString().toCharArray();
524         for (int i = 0; i < sig.length; i++) {
525             switch (sig[i]) {
526                 case '.', ';', '[', '/': sig[i] = '$';
527             }
528         }
529         String opMethodName = "op$" + new String(sig);
530         Method opMethod;
531         try {
532             // @@@ Use method handle with full power mode
533             opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName);
534         } catch (NoSuchMethodException e) {
535             return Optional.empty();
536         }
537         opMethod.setAccessible(true);
538         try {
539             FuncOp funcOp = (FuncOp) opMethod.invoke(null);
540             return Optional.of(funcOp);
541         } catch (ReflectiveOperationException e) {
542             throw new RuntimeException(e);
543         }
544     }
545 
546     /**
547      * Returns the code model of provided executable element (if any).
548      * <p>
549      * If the executable element has a code model then it will be an instance of
550      * {@code java.lang.reflect.code.op.CoreOps.FuncOp}.
551      * Note: due to circular dependencies we cannot refer to the type explicitly.
552      *
553      * @implSpec The default implementation unconditionally returns an empty optional.
554      * @param e the executable element.
555      * @return the code model of the provided executable element (if any).
556      * @since 99
557      */
558     public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
559         if (e.getModifiers().contains(Modifier.ABSTRACT) ||
560                 e.getModifiers().contains(Modifier.NATIVE)) {
561             return Optional.empty();
562         }
563 
564         Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
565         ReflectMethods reflectMethods = ReflectMethods.instance(context);
566         Attr attr = Attr.instance(context);
567         JavacElements elements = JavacElements.instance(context);
568         JavacTrees javacTrees = JavacTrees.instance(context);
569         TreeMaker make = TreeMaker.instance(context);
570         try {
571             JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
572             JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
573             ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
574             FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
575                     attribBlock -> reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make));
576             return Optional.of(op);
577         } catch (RuntimeException ex) {  // ReflectMethods.UnsupportedASTException
578             // some other error occurred when attempting to attribute the method
579             // @@@ better report of error
580             ex.printStackTrace();
581             return Optional.empty();
582         }
583     }
584 }