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      * consisting only of operations in the core dialect.
127      */
128     public interface Lowerable {
129         default Block.Builder lower(Block.Builder b) {
130             return lower(b, OpTransformer.NOOP_TRANSFORMER);
131         }
132 
133         Block.Builder lower(Block.Builder b, OpTransformer opT);
134     }
135 
136     /**
137      * An operation characteristic indicating the operation is a terminating operation
138      * occurring as the last operation in a block.
139      * <p>
140      * A terminating operation passes control to either another block within the same parent body
141      * or to that parent body.
142      */
143     public interface Terminating {
144     }
145 
146     /**
147      * An operation characteristic indicating the operation is a body terminating operation
148      * occurring as the last operation in a block.
149      * <p>
150      * A body terminating operation passes control back to its nearest ancestor body.
151      */
152     public interface BodyTerminating extends Terminating {
153     }
154 
155     /**
156      * An operation characteristic indicating the operation is a block terminating operation
157      * occurring as the last operation in a block.
158      * <p>
159      * The operation has one or more successors to other blocks within the same parent body, and passes
160      * control to one of those blocks.
161      */
162     public interface BlockTerminating extends Terminating {
163         List<Block.Reference> successors();
164     }
165 
166     /**
167      * A value that is the result of an operation.
168      */
169     public static final class Result extends Value {
170         final Op op;
171 
172         Result(Block block, Op op) {
173             super(block, op.resultType());
174 
175             this.op = op;
176         }
177 
178         @Override
179         public String toString() {
180             return "%result@" + Integer.toHexString(hashCode());
181         }
182 
183         @Override
184         public Set<Value> dependsOn() {
185             Set<Value> depends = new LinkedHashSet<>(op.operands());
186             if (op instanceof Terminating) {
187                 op.successors().stream().flatMap(h -> h.arguments().stream()).forEach(depends::add);
188             }
189 
190             return Collections.unmodifiableSet(depends);
191         }
192 
193         /**
194          * Returns the result's operation.
195          *
196          * @return the result's operation.
197          */
198         public Op op() {
199             return op;
200         }
201     }
202 
203     // Set when op is bound to block, otherwise null when unbound
204     Result result;
205 
206     // null if not specified
207     Location location;
208 
209     final String name;
210 
211     final List<Value> operands;
212 
213     /**
214      * Constructs an operation by copying given operation.
215      *
216      * @param that the operation to copy.
217      * @param cc   the copy context.
218      * @implSpec The default implementation calls the constructor with the operation's name, result type, and a list
219      * values computed, in order, by mapping the operation's operands using the copy context.
220      */
221     protected Op(Op that, CopyContext cc) {
222         this(that.name, cc.getValues(that.operands));
223         this.location = that.location;
224     }
225 
226     /**
227      * Copies this operation and its bodies, if any.
228      * <p>
229      * The returned operation is structurally identical to this operation and is otherwise independent
230      * of the values declared and used.
231      *
232      * @return the copied operation.
233      */
234     public Op copy() {
235         return transform(CopyContext.create(), OpTransformer.COPYING_TRANSFORMER);
236     }
237 
238     /**
239      * Copies this operation and its bodies, if any.
240      * <p>
241      * The returned operation is structurally identical to this operation and is otherwise independent
242      * of the values declared and used.
243      *
244      * @param cc the copy context.
245      * @return the copied operation.
246      */
247     public Op copy(CopyContext cc) {
248         return transform(cc, OpTransformer.COPYING_TRANSFORMER);
249     }
250 
251     /**
252      * Copies this operation and transforms its bodies, if any.
253      * <p>
254      * Bodies are {@link Body#transform(CopyContext, OpTransformer) transformed} with the given copy context and
255      * operation transformer.
256      *
257      * @param cc the copy context.
258      * @param ot the operation transformer.
259      * @return the transformed operation.
260      */
261     public abstract Op transform(CopyContext cc, OpTransformer ot);
262 
263     /**
264      * Constructs an operation with a name and list of operands.
265      *
266      * @param name       the operation name.
267      * @param operands   the list of operands, a copy of the list is performed if required.
268      */
269     protected Op(String name, List<? extends Value> operands) {
270         this.name = name;
271         this.operands = List.copyOf(operands);
272     }
273 
274     /**
275      * Sets the originating source location of this operation, if unbound.
276      *
277      * @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
278      * @throws IllegalStateException if this operation is bound
279      */
280     public final void setLocation(Location l) {
281         // @@@ Fail if location != null?
282         if (result != null && result.block.isBound()) {
283             throw new IllegalStateException();
284         }
285 
286         location = l;
287     }
288 
289     /**
290      * {@return the originating source location of this operation, otherwise {@code null} if not specified}
291      */
292     public final Location location() {
293         return location;
294     }
295 
296     /**
297      * Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block.
298      *
299      * @return operation's parent block, or {@code null} if the operation is not assigned to a block.
300      */
301     @Override
302     public final Block parent() {
303         if (result == null) {
304             return null;
305         }
306 
307         if (!result.block.isBound()) {
308             throw new IllegalStateException("Parent block is partially constructed");
309         }
310 
311         return result.block;
312     }
313 
314     @Override
315     public final List<Body> children() {
316         return bodies();
317     }
318 
319     /**
320      * {@return the operation's bodies, as an unmodifiable list}
321      * @implSpec this implementation returns an unmodifiable empty list.
322      */
323     public List<Body> bodies() {
324         return List.of();
325     }
326 
327     /**
328      * Returns the operation's result, otherwise {@code null} if the operation is not assigned to a block.
329      *
330      * @return the operation's result, or {@code null} if not assigned to a block.
331      */
332     public final Result result() {
333         return result;
334     }
335 
336     /**
337      * {@return the operation name}
338      */
339     public String opName() {
340         return name;
341     }
342 
343     /**
344      * {@return the operation's operands, as an unmodifiable list}
345      */
346     public List<Value> operands() {
347         return operands;
348     }
349 
350     /**
351      * {@return the operation's successors, as an unmodifiable list}
352      */
353     public List<Block.Reference> successors() {
354         return List.of();
355     }
356 
357     /**
358      * {@return the operation's result type}
359      */
360     public abstract TypeElement resultType();
361 
362     /**
363      * Returns the operation's function type.
364      * <p>
365      * The function type's result type is the operation's result type and the function type's parameter types are the
366      * operation's operand types, in order.
367      *
368      * @return the function type
369      */
370     public FunctionType opType() {
371         List<TypeElement> operandTypes = operands.stream().map(Value::type).toList();
372         return CoreType.functionType(resultType(), operandTypes);
373     }
374 
375     /**
376      * Externalizes the operation's state as a map of attributes.
377      *
378      * <p>A null attribute value is represented by the constant
379      * value {@link jdk.incubator.code.extern.ExternalizedOp#NULL_ATTRIBUTE_VALUE}.
380      *
381      * @return the operation's externalized state, as an unmodifiable map
382      */
383     public Map<String, Object> externalize() {
384         return Map.of();
385     }
386 
387     /**
388      * Traverse the operands of this operation that are the results of prior operations, recursively.
389      * <p>
390      * Traversal is performed in pre-order, reporting the operation of each operand to the visitor.
391      *
392      * @param t   the traversing accumulator
393      * @param v   the visitor
394      * @param <T> accumulator type
395      * @return the traversing accumulator
396      * @apiNote A visitor that implements the abstract method of {@code OpVisitor} and does not override any
397      * other default method will only visit operations. As such a lambda expression or method reference
398      * may be used to visit operations.
399      */
400     public final <T> T traverseOperands(T t, BiFunction<T, Op, T> v) {
401         for (Value arg : operands()) {
402             if (arg instanceof Result or) {
403                 t = v.apply(t, or.op);
404                 t = or.op.traverseOperands(t, v);
405             }
406         }
407 
408         return t;
409     }
410 
411     /**
412      * Computes values captured by this operation. A captured value is a value that dominates
413      * this operation and is used by a descendant operation.
414      * <p>
415      * The order of the captured values is first use encountered in depth
416      * first search of this operation's descendant operations.
417      *
418      * @return the list of captured values, modifiable
419      * @see Body#capturedValues()
420      */
421     public List<Value> capturedValues() {
422         Set<Value> cvs = new LinkedHashSet<>();
423 
424         capturedValues(cvs, new ArrayDeque<>(), this);
425         return new ArrayList<>(cvs);
426     }
427 
428     static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Op op) {
429         for (Body childBody : op.bodies()) {
430             Body.capturedValues(capturedValues, bodyStack, childBody);
431         }
432     }
433 
434     /**
435      * Returns the textual form of this operation.
436      *
437      * @return the textual form of this operation.
438      */
439     public String toText() {
440         return OpWriter.toText(this);
441     }
442 
443 
444     /**
445      * Returns the quoted code model of the given quotable reference, if present.
446      *
447      * @param q the quotable reference.
448      * @return the quoted code model or an empty optional if the
449      *         quoted code model is unavailable.
450      * @apiNote If the quotable reference is a proxy instance, then the
451      *          quoted code model is unavailable and this method
452      *          returns an empty optional.
453      * @since 99
454      */
455     public static Optional<Quoted> ofQuotable(Quotable q) {
456         Object oq = q;
457         if (Proxy.isProxyClass(oq.getClass())) {
458             // @@@ The interpreter implements interpretation of
459             // lambdas using a proxy whose invocation handler
460             // supports the internal protocol to access the quoted instance
461             oq = Proxy.getInvocationHandler(oq);
462         }
463 
464         Method method;
465         try {
466             method = oq.getClass().getDeclaredMethod("__internal_quoted");
467         } catch (NoSuchMethodException e) {
468             return Optional.empty();
469         }
470         method.setAccessible(true);
471 
472         Quoted quoted;
473         try {
474             quoted = (Quoted) method.invoke(oq);
475         } catch (InvocationTargetException | IllegalAccessException e) {
476             throw new RuntimeException(e);
477         }
478         return Optional.of(quoted);
479     }
480 
481     /**
482      * Returns the code model of the given method's body, if present.
483      *
484      * @param method the method.
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         char[] sig = MethodRef.method(method).toString().toCharArray();
499         for (int i = 0; i < sig.length; i++) {
500             switch (sig[i]) {
501                 case '.', ';', '[', '/': sig[i] = '$';
502             }
503         }
504         String opMethodName = new String(sig);
505         Method opMethod;
506         try {
507             // @@@ Use method handle with full power mode
508             opMethod = method.getDeclaringClass().getDeclaredMethod(opMethodName);
509         } catch (NoSuchMethodException e) {
510             return Optional.empty();
511         }
512         opMethod.setAccessible(true);
513         try {
514             FuncOp funcOp = (FuncOp) opMethod.invoke(null);
515             return Optional.of(funcOp);
516         } catch (ReflectiveOperationException e) {
517             throw new RuntimeException(e);
518         }
519     }
520 
521     /**
522      * Returns the code model of provided executable element (if any).
523      * <p>
524      * If the executable element has a code model then it will be an instance of
525      * {@code java.lang.reflect.code.op.CoreOps.FuncOp}.
526      * Note: due to circular dependencies we cannot refer to the type explicitly.
527      *
528      * @implSpec The default implementation unconditionally returns an empty optional.
529      * @param e the executable element.
530      * @return the code model of the provided executable element (if any).
531      * @since 99
532      */
533     public static Optional<FuncOp> ofElement(ProcessingEnvironment processingEnvironment, ExecutableElement e) {
534         if (e.getModifiers().contains(Modifier.ABSTRACT) ||
535                 e.getModifiers().contains(Modifier.NATIVE)) {
536             return Optional.empty();
537         }
538 
539         Context context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
540         ReflectMethods reflectMethods = ReflectMethods.instance(context);
541         Attr attr = Attr.instance(context);
542         JavacElements elements = JavacElements.instance(context);
543         JavacTrees javacTrees = JavacTrees.instance(context);
544         TreeMaker make = TreeMaker.instance(context);
545         try {
546             JCMethodDecl methodTree = (JCMethodDecl)elements.getTree(e);
547             JavacScope scope = javacTrees.getScope(javacTrees.getPath(e));
548             ClassSymbol enclosingClass = (ClassSymbol) scope.getEnclosingClass();
549             FuncOp op = attr.runWithAttributedMethod(scope.getEnv(), methodTree,
550                     attribBlock -> reflectMethods.getMethodBody(enclosingClass, methodTree, attribBlock, make));
551             return Optional.of(op);
552         } catch (RuntimeException ex) {  // ReflectMethods.UnsupportedASTException
553             // some other error occurred when attempting to attribute the method
554             // @@@ better report of error
555             ex.printStackTrace();
556             return Optional.empty();
557         }
558     }
559 }