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