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