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