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